diff --git a/CHANGELOG b/CHANGELOG index ad020e7dd..f6a03426e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ #Nmap Changelog ($Id$); -*-text-*- +o [NSE][GH#1614] Add TLS support to rdp-enum-encryption. Enables determining + protocol version against servers that require TLS and lays ground work for + some NLA/CredSSP information collection. [Tom Sellers] + o [NSE][GH#1611] Address two protocol parsing issues in rdp-enum-encryption and the RDP nse library which broke scanning of Windows XP. Clarify protocol types [Tom Sellers] diff --git a/nselib/rdp.lua b/nselib/rdp.lua index 9ec377a73..03c4858ce 100644 --- a/nselib/rdp.lua +++ b/nselib/rdp.lua @@ -10,6 +10,7 @@ local nmap = require("nmap") local stdnse = require("stdnse") local string = require "string" +local asn1 = require "asn1" _ENV = stdnse.module("rdp", stdnse.seeall) -- Server Core Data 2.2.1.4.2 @@ -26,6 +27,26 @@ PROTO_VERSION = { [0x0008000C] = " RDP 10.7 server", } +-- T.125 Result enumerated type +CONNECT_RESPONSE_RESULT = { + [ 0] = "rt-successful", + [ 1] = "rt-domain-merging", + [ 2] = "rt-domain-not-hierarchical", + [ 3] = "rt-no-such-channel", + [ 4] = "rt-no-such-domain", + [ 5] = "rt-no-such-user", + [ 6] = "rt-not-admitted", + [ 7] = "rt-other-user-id", + [ 8] = "rt-parameters-unacceptable", + [ 9] = "rt-token-not-available", + [10] = "rt-token-not-possessed", + [11] = "rt-too-many-channels", + [12] = "rt-too-many-tokens", + [13] = "rt-too-many-users", + [14] = "rt-unspecified-failure", + [15] = "rt-user-rejected", +} + Packet = { TPKT = { @@ -105,7 +126,7 @@ Packet = { ConfCreateResponse = { - new = function(self, data) + new = function(self) local o = {} setmetatable(o, self) self.__index = self @@ -113,23 +134,55 @@ Packet = { end, parse = function(data) - local ccr = Packet.ConfCreateResponse:new() - local pos = 67 - while data:len() > pos do - block_type, block_len = string.unpack(" pos do + block_type, block_len = string.unpack("I2I2B", 0x0000, -- dst reference @@ -172,8 +223,8 @@ Request = { MCSConnectInitial = { - new = function(self, cipher) - local o = { cipher = cipher } + new = function(self, cipher, server_proto) + local o = { cipher = cipher, server_proto = server_proto } setmetatable(o, self) self.__index = self return o @@ -183,7 +234,7 @@ Request = { local data = stdnse.fromhex( "7f 65" .. -- BER: Application-Defined Type = APPLICATION 101, - "82 01 90" .. -- BER: Type Length = 404 bytes + "82 01 94" .. -- BER: Type Length = 404 bytes "04 01 01" .. -- Connect-Initial::callingDomainSelector "04 01 01" .. -- Connect-Initial::calledDomainSelector "01 01 ff" .. -- Connect-Initial::upwardFlag = TRUE @@ -214,20 +265,21 @@ Request = { "02 01 01" .. -- DomainParameters::maxHeight = 1 "02 02 ff ff" .. -- DomainParameters::maxMCSPDUsize = 65535 "02 01 02" .. -- DomainParameters::protocolVersion = 2 - "04 82 01 2f" .. -- Connect-Initial::userData (307 bytes) + "04 82 01 33" .. -- Connect-Initial::userData (307 bytes) "00 05" .. -- object length = 5 bytes "00 14 7c 00 01" .. -- object - "81 26" .. -- ConnectData::connectPDU length = 298 bytes - "00 08 00 10 00 01 c0 00 44 75 63 61 81 18" .. -- PER encoded (ALIGNED variant of BASIC-PER) GCC Conference Create Request PDU - "01 c0 d4 00" .. -- TS_UD_HEADER::type = CS_CORE (0xc001), length = 216 bytes + "81 2a" .. -- ConnectData::connectPDU length = 42 bytes + "00 08 00 10 00 01 c0 00 44 75 63 61 81 1c" .. -- PER encoded (ALIGNED variant of BASIC-PER) GCC Conference Create Request PDU + "01 c0 d8 00" .. -- TS_UD_HEADER::type = CS_CORE (0xc001), length = 216 bytes "04 00 08 00" .. -- TS_UD_CS_CORE::version = 0x0008004 "00 05" .. -- TS_UD_CS_CORE::desktopWidth = 1280 "20 03" .. -- TS_UD_CS_CORE::desktopHeight = 1024 "01 ca" .. -- TS_UD_CS_CORE::colorDepth = RNS_UD_COLOR_8BPP (0xca01) "03 aa" .. -- TS_UD_CS_CORE::SASSequence - "09 08 00 00" .. -- TS_UD_CS_CORE::keyboardLayout = 0x409 = 1033 = English (US) - "28 0a 00 00" .. -- TS_UD_CS_CORE::clientBuild = 3790 - "45 00 4d 00 50 00 2d 00 4c 00 41 00 50 00 2d 00 30 00 30 00 31 00 34 00 00 00 00 00 00 00 00 00" .. -- TS_UD_CS_CORE::clientName = ELTONS-TEST2 + "09 04 00 00" .. -- TS_UD_CS_CORE::keyboardLayout = 0x409 = 1033 = English (US) + "28 0a 00 00" .. -- TS_UD_CS_CORE::clientBuild = 2600 + "45 00 4d 00 50 00 2d 00 4c 00 41 00 50 00 2d 00 " .. + "30 00 30 00 31 00 34 00 00 00 00 00 00 00 00 00 " .. -- TS_UD_CS_CORE::clientName = EMP-LAP-0014 "04 00 00 00" .. -- TS_UD_CS_CORE::keyboardType "00 00 00 00" .. -- TS_UD_CS_CORE::keyboardSubtype "0c 00 00 00" .. -- TS_UD_CS_CORE::keyboardFunctionKey @@ -238,16 +290,17 @@ Request = { "01 ca" .. -- TS_UD_CS_CORE::postBeta2ColorDepth = RNS_UD_COLOR_8BPP (0xca01) "01 00" .. -- TS_UD_CS_CORE::clientProductId "00 00 00 00" .. -- TS_UD_CS_CORE::serialNumber - "10 00" .. -- TS_UD_CS_CORE::highColorDepth = 24 bpp - "07 00" .. -- TS_UD_CS_CORE::supportedColorDepths + "18 00" .. -- TS_UD_CS_CORE::highColorDepth = 24 bpp + "07 00" .. -- TS_UD_CS_CORE::supportedColorDepths = 24 bpp "01 00" .. -- TS_UD_CS_CORE::earlyCapabilityFlags - "36 00 39 00 37 00 31 00 32 00 2d 00 37 00 38 00 " .. - "33 00 2d 00 30 00 33 00 35 00 37 00 39 00 37 00 " .. - "34 00 2d 00 34 00 32 00 37 00 31 00 34 00 00 00 " .. - "00 00 00 00 00 00 00 00 00 00 00 00 " .. -- TS_UD_CS_CORE::clientDigProductId = "69712-783-0357974-42714" + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " .. + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " .. + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " .. + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " .. -- TS_UD_CS_CORE::clientDigProductId "00" .. -- TS_UD_CS_CORE::connectionType = 0 (not used as RNS_UD_CS_VALID_CONNECTION_TYPE not set) - "00" .. -- TS_UD_CS_CORE::pad1octet - "00 00 00 00" .. -- TS_UD_CS_CORE::serverSelectedProtocol + "00") -- TS_UD_CS_CORE::pad1octet + -- TS_UD_CS_CORE::serverSelectedProtocol + .. string.pack(" 0 ) then + local err = string.unpack("B", response.itut.data, 5) + if ( err > 0 ) then + table.insert(res_proto, ("%s: FAILED (%s)"):format(k, ERRORS[err] or "Unknown")) + else + table.insert(res_proto, ("%s: FAILED"):format(k)) + end + end + else + -- rdpNegData, which contains the negotiation response or failure, + -- is optional. WinXP SP3 does not return this section which means + -- we can't tell if the protocol is accepted or not. + table.insert(res_proto, ("%s: Unknown"):format(k)) + end + else + comm:close() return false, response end - if response.itut.data ~= "" then - local success = string.unpack("B", response.itut.data) - - if ( success == 2 ) then - table.insert(res_proto, ("%s: SUCCESS"):format(k)) - elseif ( nmap.debugging() > 0 ) then - local err = string.unpack("B", response.itut.data, 5) - if ( err > 0 ) then - table.insert(res_proto, ("%s: FAILED (%s)"):format(k, ERRORS[err] or "Unknown")) - else - table.insert(res_proto, ("%s: FAILED"):format(k)) + -- For servers that require TLS or NLA the only way to get the RDP protocol + -- version to negotiate TLS or NLA. This section does that for TLS. There + -- is no NLA currently. + if status and (v == 1) then + local res, _ = comm.socket:reconnect_ssl() + if res then + local msc = rdp.Request.MCSConnectInitial:new(0, 1) + status, response = comm:exch(msc) + if status then + if response.ccr.proto_version then + proto_version = response.ccr.proto_version + end end end - else - -- rdpNegData, which contains the negotiaion response or failure, - -- is optional. WinXP SP3 does not return this section which means - -- we can't tell if the protocol is accepted or not. - table.insert(res_proto, ("%s: Unknown"):format(k)) end + + comm:close() end table.sort(res_proto) - return true, res_proto + return true, res_proto, proto_version end local function enum_ciphers(host, port) @@ -133,19 +156,22 @@ local function enum_ciphers(host, port) end for k, v in get_ordered_ciphers() do + -- Prevent reconnecting too quickly, improves reliability + stdnse.sleep(0.2) + local comm = rdp.Comm:new(host, port) if ( not(comm:connect()) ) then return false, fail("Failed to connect to server") end local cr = rdp.Request.ConnectionRequest:new() - local status, response = comm:exch(cr) + local status, _ = comm:exch(cr) if ( not(status) ) then break end local msc = rdp.Request.MCSConnectInitial:new(v) - local status, response = comm:exch(msc) + status, response = comm:exch(msc) comm:close() if ( status ) then if ( response.ccr and response.ccr.enc_cipher == v ) then @@ -166,20 +192,22 @@ end action = function(host, port) local result = {} - local status, res_proto = enum_protocols(host, port) + local status, res_proto, proto_ver = enum_protocols(host, port) if ( not(status) ) then return res_proto end - local status, res_ciphers, proto_version = enum_ciphers(host, port) + local status, res_ciphers, cipher_ver = enum_ciphers(host, port) if ( not(status) ) then return res_ciphers end table.insert(result, res_proto) table.insert(result, res_ciphers) - if proto_version then - table.insert(result, proto_version) + if proto_ver then + table.insert(result, proto_ver) + elseif cipher_ver then + table.insert(result, cipher_ver) end return stdnse.format_output(true, result) end