diff --git a/lib/rex/proto/mssql/client_mixin.rb b/lib/rex/proto/mssql/client_mixin.rb index 2c00c7ba869e..684b9c529e9c 100644 --- a/lib/rex/proto/mssql/client_mixin.rb +++ b/lib/rex/proto/mssql/client_mixin.rb @@ -175,7 +175,6 @@ def mssql_parse_tds_reply(data, info) 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 @@ -195,6 +194,50 @@ def mssql_parse_tds_reply(data, info) col[:value_length] = data.slice!(0, 2).unpack('v')[0] col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '') + when 109 + col[:id] = :float + col[:value_length] = data.slice!(0, 1).unpack('C')[0] + + when 108 + col[:id] = :numeric + col[:value_length] = data.slice!(0, 1).unpack('C')[0] + col[:precision] = data.slice!(0, 1).unpack('C')[0] + col[:scale] = data.slice!(0, 1).unpack('C')[0] + + when 60 + col[:id] = :money + + when 110 + col[:value_length] = data.slice!(0, 1).unpack('C')[0] + case col[:value_length] + when 8 + col[:id] = :money + when 4 + col[:id] = :smallmoney + else + col[:id] = :unknown + end + + when 111 + col[:value_length] = data.slice!(0, 1).unpack('C')[0] + case col[:value_length] + when 4 + col[:id] = :smalldatetime + when 8 + col[:id] = :datetime + else + col[:id] = :unknown + end + + when 122 + col[:id] = :smallmoney + + when 59 + col[:id] = :float + + when 58 + col[:id] = :smalldatetime + when 36 col[:id] = :guid col[:value_length] = data.slice!(0, 1).unpack('C')[0] @@ -352,9 +395,85 @@ def mssql_parse_tds_row(data, info) end row << str + when :float + datalen = data.slice!(0, 1).unpack('C')[0] + case datalen + when 8 + row << data.slice!(0, datalen).unpack('E')[0] + when 4 + row << data.slice!(0, datalen).unpack('e')[0] + else + row << nil + end + + when :numeric + varlen = data.slice!(0, 1).unpack('C')[0] + if varlen == 0 + row << nil + else + sign = data.slice!(0, 1).unpack('C')[0] + raw = data.slice!(0, varlen - 1) + value = '' + + case varlen + when 5 + value = raw.unpack('L')[0]/(10**col[:scale]).to_f + when 9 + value = raw.unpack('Q')[0]/(10**col[:scale]).to_f + when 13 + chunks = raw.unpack('L3') + value = chunks[2] << 64 | chunks[1] << 32 | chunks[0] + value /= (10**col[:scale]).to_f + when 17 + chunks = raw.unpack('L4') + value = chunks[3] << 96 | chunks[2] << 64 | chunks[1] << 32 | chunks[0] + value /= (10**col[:scale]).to_f + end + case sign + when 1 + row << value + when 0 + row << value * -1 + end + end + + when :money + datalen = data.slice!(0, 1).unpack('C')[0] + if datalen == 0 + row << nil + else + raw = data.slice!(0, datalen) + rev = raw.slice(4, 4) << raw.slice(0, 4) + row << rev.unpack('q')[0]/10000.0 + end + + when :smallmoney + datalen = data.slice!(0, 1).unpack('C')[0] + if datalen == 0 + row << nil + else + row << data.slice!(0, datalen).unpack('l')[0] / 10000.0 + end + + when :smalldatetime + datalen = data.slice!(0, 1).unpack('C')[0] + if datalen == 0 + row << nil + else + days = data.slice!(0, 2).unpack('S')[0] + minutes = data.slice!(0, 2).unpack('S')[0] / 1440.0 + row << DateTime.new(1900, 1, 1) + days + minutes + end when :datetime - row << data.slice!(0, 8).unpack("H*")[0] + datalen = data.slice!(0, 1).unpack('C')[0] + if datalen == 0 + row << nil + else + days = data.slice!(0, 4).unpack('l')[0] + minutes = data.slice!(0, 4).unpack('l')[0] / 1440.0 + row << DateTime.new(1900, 1, 1) + days + minutes + end when :rawint row << data.slice!(0, 4).unpack('V')[0] diff --git a/test/modules/post/test/mssql.rb b/test/modules/post/test/mssql.rb index 9619ce5b70f4..bc46f78a79c0 100644 --- a/test/modules/post/test/mssql.rb +++ b/test/modules/post/test/mssql.rb @@ -42,11 +42,31 @@ def test_console_query end def test_datatypes - it "should support ntext TDS datatype" do - stdout = with_mocked_console(session) {|console| console.run_single(%{ query "select cast('foo' as ntext);"})} - ret = true - ret &&= stdout.buf.match?(/0 foo/) - ret + [ + {query: "select cast('1990-01-02' as datetime);", expected: [[DateTime.new(1990, 1, 2)]]}, + {query: "select cast(null as datetime);", expected: [[nil]]}, + {query: "select cast('1990-01-02' as smalldatetime);", expected: [[DateTime.new(1990, 1, 2)]]}, + {query: "select cast(null as smalldatetime);", expected: [[nil]]}, + {query: "select cast('19900' as float);", expected: [[19900.0]]}, + {query: "select cast(null as float);", expected: [[nil]]}, + {query: "select cast('19900' as real);", expected: [[19900.0]]}, + {query: "select cast(null as real);", expected: [[nil]]}, + {query: "select cast('12.50' as money);", expected: [[12.5]]}, + {query: "select cast(null as money);", expected: [[nil]]}, + {query: "select cast('12.50' as smallmoney);", expected: [[12.5]]}, + {query: "select cast(null as smallmoney);", expected: [[nil]]}, + {query: "select cast('1999999900' as numeric(16, 6));", expected: [[1999999900.0]]}, + {query: "select cast(null as numeric(16, 6));", expected: [[nil]]}, + {query: "select cast('foo' as ntext);", expected: [['foo']]}, + {query: "select cast(null as ntext);", expected: [[nil]]}, + ].each do |test| + it "should execute the query #{test[:query]} and return #{test[:expected].inspect}" do + console = session.console + result = console.client.query(test[:query]) + ret = result[:rows] == test[:expected] + ret &&= result[:errors].empty? + ret + end end end