1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-07 21:21:31 +00:00

Add TLS support to rdp-enum-encryption Closes #1614

This commit is contained in:
tomsellers
2019-06-04 19:38:28 +00:00
parent 43b9461e5c
commit a4f3c85eb9
3 changed files with 141 additions and 58 deletions

View File

@@ -1,5 +1,9 @@
#Nmap Changelog ($Id$); -*-text-*- #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 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 the RDP nse library which broke scanning of Windows XP. Clarify protocol
types [Tom Sellers] types [Tom Sellers]

View File

@@ -10,6 +10,7 @@
local nmap = require("nmap") local nmap = require("nmap")
local stdnse = require("stdnse") local stdnse = require("stdnse")
local string = require "string" local string = require "string"
local asn1 = require "asn1"
_ENV = stdnse.module("rdp", stdnse.seeall) _ENV = stdnse.module("rdp", stdnse.seeall)
-- Server Core Data 2.2.1.4.2 -- Server Core Data 2.2.1.4.2
@@ -26,6 +27,26 @@ PROTO_VERSION = {
[0x0008000C] = " RDP 10.7 server", [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 = { Packet = {
TPKT = { TPKT = {
@@ -105,7 +126,7 @@ Packet = {
ConfCreateResponse = { ConfCreateResponse = {
new = function(self, data) new = function(self)
local o = {} local o = {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
@@ -113,23 +134,55 @@ Packet = {
end, end,
parse = function(data) parse = function(data)
local ccr = Packet.ConfCreateResponse:new()
local pos = 67
while data:len() > pos do local tag_decoder = {}
block_type, block_len = string.unpack("<I2I2", data, pos)
tag_decoder["\x0A"] = function( self, encStr, elen, pos )
return self.decodeInt(encStr, elen, pos)
end
local ccr = Packet.ConfCreateResponse:new()
local decoder = asn1.ASN1Decoder:new()
decoder:registerTagDecoders( tag_decoder )
local _, pos = decoder.decodeLength(data, 3)
local response_result, userdata
response_result, pos = decoder:decode(data, pos)
ccr.result = CONNECT_RESPONSE_RESULT[response_result]
ccr.calledConnectId, pos = decoder:decode(data, pos)
-- T.125 DomainParameters SEQUENCE
-- Not interested in its values now, just need to correctly parse
-- the block so we can arrive at userData
_, pos = decoder:decode(data, pos)
-- T.125 userData OCTO string
userdata, _ = decoder:decode(data, pos)
if userdata == nil then
return ccr
end
-- Hackery to avoid writing ASN.1 PER decoding. Skip over fixed length
-- T.124 ConnectData header. Decode the length since it can be multiple
-- bytes. Drops us where we need to be.
_, pos = asn1.ASN1Decoder.decodeLength(userdata, 22 )
local block_type, block_len
while userdata:len() > pos do
block_type, block_len = string.unpack("<I2I2", userdata, pos)
if block_type == 0x0c01 then if block_type == 0x0c01 then
-- 2.2.1.42 Server Core Data - TS_UD_SC_CORE -- 2.2.1.42 Server Core Data - TS_UD_SC_CORE
proto_ver = string.unpack("<I4", data, pos + 4) local proto_ver = string.unpack("<I4",userdata, pos + 4)
ccr.proto_version = ("RDP Protocol Version: %s"):format(PROTO_VERSION[proto_ver] or "Unknown") ccr.proto_version = ("RDP Protocol Version: %s"):format(PROTO_VERSION[proto_ver] or "Unknown")
elseif block_type == 0x0c02 then elseif block_type == 0x0c02 then
-- 2.2.1.4.3 Server Security Data - TS_UD_SC_SEC1 -- 2.2.1.4.3 Server Security Data - TS_UD_SC_SEC1
ccr.enc_level = string.unpack("B", data, pos + 8) ccr.enc_level = string.unpack("B", userdata, pos + 8)
ccr.enc_cipher= string.unpack("B", data, pos + 4) ccr.enc_cipher= string.unpack("B", userdata, pos + 4)
end end
pos = pos + block_len pos = pos + block_len
end end
return ccr return ccr
end, end,
}, },
@@ -149,8 +202,6 @@ Request = {
__tostring = function(self) __tostring = function(self)
local cookie = "mstshash=nmap" local cookie = "mstshash=nmap"
local itpkt_len = 21 + #cookie
local itut_len = 16 + #cookie
local data = string.pack(">I2I2B", local data = string.pack(">I2I2B",
0x0000, -- dst reference 0x0000, -- dst reference
@@ -172,8 +223,8 @@ Request = {
MCSConnectInitial = { MCSConnectInitial = {
new = function(self, cipher) new = function(self, cipher, server_proto)
local o = { cipher = cipher } local o = { cipher = cipher, server_proto = server_proto }
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
@@ -183,7 +234,7 @@ Request = {
local data = stdnse.fromhex( local data = stdnse.fromhex(
"7f 65" .. -- BER: Application-Defined Type = APPLICATION 101, "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::callingDomainSelector
"04 01 01" .. -- Connect-Initial::calledDomainSelector "04 01 01" .. -- Connect-Initial::calledDomainSelector
"01 01 ff" .. -- Connect-Initial::upwardFlag = TRUE "01 01 ff" .. -- Connect-Initial::upwardFlag = TRUE
@@ -214,20 +265,21 @@ Request = {
"02 01 01" .. -- DomainParameters::maxHeight = 1 "02 01 01" .. -- DomainParameters::maxHeight = 1
"02 02 ff ff" .. -- DomainParameters::maxMCSPDUsize = 65535 "02 02 ff ff" .. -- DomainParameters::maxMCSPDUsize = 65535
"02 01 02" .. -- DomainParameters::protocolVersion = 2 "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 05" .. -- object length = 5 bytes
"00 14 7c 00 01" .. -- object "00 14 7c 00 01" .. -- object
"81 26" .. -- ConnectData::connectPDU length = 298 bytes "81 2a" .. -- ConnectData::connectPDU length = 42 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 "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 d4 00" .. -- TS_UD_HEADER::type = CS_CORE (0xc001), length = 216 bytes "01 c0 d8 00" .. -- TS_UD_HEADER::type = CS_CORE (0xc001), length = 216 bytes
"04 00 08 00" .. -- TS_UD_CS_CORE::version = 0x0008004 "04 00 08 00" .. -- TS_UD_CS_CORE::version = 0x0008004
"00 05" .. -- TS_UD_CS_CORE::desktopWidth = 1280 "00 05" .. -- TS_UD_CS_CORE::desktopWidth = 1280
"20 03" .. -- TS_UD_CS_CORE::desktopHeight = 1024 "20 03" .. -- TS_UD_CS_CORE::desktopHeight = 1024
"01 ca" .. -- TS_UD_CS_CORE::colorDepth = RNS_UD_COLOR_8BPP (0xca01) "01 ca" .. -- TS_UD_CS_CORE::colorDepth = RNS_UD_COLOR_8BPP (0xca01)
"03 aa" .. -- TS_UD_CS_CORE::SASSequence "03 aa" .. -- TS_UD_CS_CORE::SASSequence
"09 08 00 00" .. -- TS_UD_CS_CORE::keyboardLayout = 0x409 = 1033 = English (US) "09 04 00 00" .. -- TS_UD_CS_CORE::keyboardLayout = 0x409 = 1033 = English (US)
"28 0a 00 00" .. -- TS_UD_CS_CORE::clientBuild = 3790 "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 = ELTONS-TEST2 "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 "04 00 00 00" .. -- TS_UD_CS_CORE::keyboardType
"00 00 00 00" .. -- TS_UD_CS_CORE::keyboardSubtype "00 00 00 00" .. -- TS_UD_CS_CORE::keyboardSubtype
"0c 00 00 00" .. -- TS_UD_CS_CORE::keyboardFunctionKey "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 ca" .. -- TS_UD_CS_CORE::postBeta2ColorDepth = RNS_UD_COLOR_8BPP (0xca01)
"01 00" .. -- TS_UD_CS_CORE::clientProductId "01 00" .. -- TS_UD_CS_CORE::clientProductId
"00 00 00 00" .. -- TS_UD_CS_CORE::serialNumber "00 00 00 00" .. -- TS_UD_CS_CORE::serialNumber
"10 00" .. -- TS_UD_CS_CORE::highColorDepth = 24 bpp "18 00" .. -- TS_UD_CS_CORE::highColorDepth = 24 bpp
"07 00" .. -- TS_UD_CS_CORE::supportedColorDepths "07 00" .. -- TS_UD_CS_CORE::supportedColorDepths = 24 bpp
"01 00" .. -- TS_UD_CS_CORE::earlyCapabilityFlags "01 00" .. -- TS_UD_CS_CORE::earlyCapabilityFlags
"36 00 39 00 37 00 31 00 32 00 2d 00 37 00 38 00 " .. "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " ..
"33 00 2d 00 30 00 33 00 35 00 37 00 39 00 37 00 " .. "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 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 00 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 " .. -- 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::connectionType = 0 (not used as RNS_UD_CS_VALID_CONNECTION_TYPE not set)
"00" .. -- TS_UD_CS_CORE::pad1octet "00") -- TS_UD_CS_CORE::pad1octet
"00 00 00 00" .. -- TS_UD_CS_CORE::serverSelectedProtocol -- TS_UD_CS_CORE::serverSelectedProtocol
.. string.pack("<I4", self.server_proto or 0) .. stdnse.fromhex(
"04 c0 0c 00" .. -- TS_UD_HEADER::type = CS_CLUSTER (0xc004), length = 12 bytes "04 c0 0c 00" .. -- TS_UD_HEADER::type = CS_CLUSTER (0xc004), length = 12 bytes
"09 00 00 00" .. -- TS_UD_CS_CLUSTER::Flags = 0x0d "09 00 00 00" .. -- TS_UD_CS_CLUSTER::Flags = 0x0d
"00 00 00 00" .. -- TS_UD_CS_CLUSTER::RedirectedSessionID "00 00 00 00" .. -- TS_UD_CS_CLUSTER::RedirectedSessionID
@@ -267,7 +320,7 @@ Request = {
return tostring(Packet.TPKT:new(Packet.ITUT:new(0xF0, data))) return tostring(Packet.TPKT:new(Packet.ITUT:new(0xF0, data)))
end end
} },
} }
@@ -284,7 +337,6 @@ Response = {
parse = function(data) parse = function(data)
local cc = Response.ConnectionConfirm:new() local cc = Response.ConnectionConfirm:new()
local pos, _
cc.tpkt = Packet.TPKT.parse(data) cc.tpkt = Packet.TPKT.parse(data)
cc.itut = Packet.ITUT.parse(cc.tpkt.data) cc.itut = Packet.ITUT.parse(cc.tpkt.data)
@@ -373,8 +425,7 @@ Comm = {
return false, err return false, err
end end
local data local _, data = self:recv()
status, data = self:recv()
if ( #data< 5 ) then if ( #data< 5 ) then
return false, "Packet too short" return false, "Packet too short"
end end

View File

@@ -64,19 +64,20 @@ local function enum_protocols(host, port)
} }
local res_proto = { name = "Security layer" } local res_proto = { name = "Security layer" }
local proto_version
for k, v in pairs(PROTOCOLS) do for k, v in pairs(PROTOCOLS) do
-- Prevent reconnecting too quickly, improves reliability
stdnse.sleep(0.2)
local comm = rdp.Comm:new(host, port) local comm = rdp.Comm:new(host, port)
if ( not(comm:connect()) ) then if ( not(comm:connect()) ) then
return false, fail("Failed to connect to server") return false, fail("Failed to connect to server")
end end
local cr = rdp.Request.ConnectionRequest:new(v) local cr = rdp.Request.ConnectionRequest:new(v)
local status, response = comm:exch(cr) local status, response = comm:exch(cr)
comm:close()
if ( not(status) ) then
return false, response
end
if status then
if response.itut.data ~= "" then if response.itut.data ~= "" then
local success = string.unpack("B", response.itut.data) local success = string.unpack("B", response.itut.data)
@@ -91,14 +92,36 @@ local function enum_protocols(host, port)
end end
end end
else else
-- rdpNegData, which contains the negotiaion response or failure, -- rdpNegData, which contains the negotiation response or failure,
-- is optional. WinXP SP3 does not return this section which means -- is optional. WinXP SP3 does not return this section which means
-- we can't tell if the protocol is accepted or not. -- we can't tell if the protocol is accepted or not.
table.insert(res_proto, ("%s: Unknown"):format(k)) table.insert(res_proto, ("%s: Unknown"):format(k))
end end
else
comm:close()
return false, response
end
-- 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
end
comm:close()
end end
table.sort(res_proto) table.sort(res_proto)
return true, res_proto return true, res_proto, proto_version
end end
local function enum_ciphers(host, port) local function enum_ciphers(host, port)
@@ -133,19 +156,22 @@ local function enum_ciphers(host, port)
end end
for k, v in get_ordered_ciphers() do 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) local comm = rdp.Comm:new(host, port)
if ( not(comm:connect()) ) then if ( not(comm:connect()) ) then
return false, fail("Failed to connect to server") return false, fail("Failed to connect to server")
end end
local cr = rdp.Request.ConnectionRequest:new() local cr = rdp.Request.ConnectionRequest:new()
local status, response = comm:exch(cr) local status, _ = comm:exch(cr)
if ( not(status) ) then if ( not(status) ) then
break break
end end
local msc = rdp.Request.MCSConnectInitial:new(v) local msc = rdp.Request.MCSConnectInitial:new(v)
local status, response = comm:exch(msc) status, response = comm:exch(msc)
comm:close() comm:close()
if ( status ) then if ( status ) then
if ( response.ccr and response.ccr.enc_cipher == v ) then if ( response.ccr and response.ccr.enc_cipher == v ) then
@@ -166,20 +192,22 @@ end
action = function(host, port) action = function(host, port)
local result = {} local result = {}
local status, res_proto = enum_protocols(host, port) local status, res_proto, proto_ver = enum_protocols(host, port)
if ( not(status) ) then if ( not(status) ) then
return res_proto return res_proto
end 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 if ( not(status) ) then
return res_ciphers return res_ciphers
end end
table.insert(result, res_proto) table.insert(result, res_proto)
table.insert(result, res_ciphers) table.insert(result, res_ciphers)
if proto_version then if proto_ver then
table.insert(result, proto_version) table.insert(result, proto_ver)
elseif cipher_ver then
table.insert(result, cipher_ver)
end end
return stdnse.format_output(true, result) return stdnse.format_output(true, result)
end end