diff --git a/nselib/ike.lua b/nselib/ike.lua index cb044a00b..d906e6502 100644 --- a/nselib/ike.lua +++ b/nselib/ike.lua @@ -15,14 +15,14 @@ The current funcionality includes: 1. Generating a Main or Aggressive Mode IKE request packet with a variable amount of transforms and a vpn group. 2. Sending a packet 3. Receiving the response - 4. Parsing the response for VIDs - 5. Searching for the VIDs in 'ike-fingerprints.lua' - 6. returning a parsed info table + 4. Parsing the response for VIDs + 5. Searching for the VIDs in 'ike-fingerprints.lua' + 6. returning a parsed info table This library is meant for extension, which could include: - 1. complete parsing of the response packet (might allow for better fingerprinting) - 2. adding more options to the request packet - vendor field (might give better fingerprinting of services, e.g. Checkpoint) + 1. complete parsing of the response packet (might allow for better fingerprinting) + 2. adding more options to the request packet + vendor field (might give better fingerprinting of services, e.g. Checkpoint) 3. backoff pattern analyses ... @@ -36,28 +36,47 @@ author = "Jesper Kueckelhahn" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"discovery", "safe"} -local enc_methods = { - ["des"] = 0x80010001, - ["3des"] = 0x80010005, - ["aes/128"]= { 0x80010007, 0x800E0080 }, - ["aes/192"]= { 0x80010007, 0x800E00C0 }, - ["aes/256"]= { 0x80010007, 0x800E0100 } +local enc_methods = { + ["des"] = 0x80010001, + ["3des"] = 0x80010005, + ["aes/128"] = { 0x80010007, 0x800E0080 }, + ["aes/192"] = { 0x80010007, 0x800E00C0 }, + ["aes/256"] = { 0x80010007, 0x800E0100 }, } +local authentication= { + ["psk"] = 0x80030001, + ["rsa"] = 0x80030003, + ["Hybrid"] = 0x8003FADD, + ["XAUTH"] = 0x8003FDE9, +} -local authentication= { ["psk"] = 0x80030001, ["rsa"] = 0x80030003, ["Hybrid"] = 0x8003FADD, ["XAUTH"] = 0x8003FDE9} +local hash_algo = { + ["md5"] = 0x80020001, + ["sha1"] = 0x80020002, +} -local hash_algo = { ["md5"] = 0x80020001, ["sha1"] = 0x80020002} -local group_desc = { ["768"] = 0x80040001, ["1024"] = 0x80040002, ["1536"]= 0x80040005} -local exchange_mode = { ["Main"] = 0x02, ["Aggressive"]= 0x04} -local protocol_ids = { ["tcp"] = "06", ["udp"]= "11"} +local group_desc = { + ["768"] = 0x80040001, + ["1024"] = 0x80040002, + ["1536"] = 0x80040005, +} +local exchange_mode = { + ["Main"] = 0x02, + ["Aggressive"] = 0x04, +} + +local protocol_ids = { + ["tcp"] = "06", + ["udp"] = "11", +} -- Response packet types local response_exchange_type = { ["02"] = "Main", ["04"] = "Aggressive", - ["05"] = "Informational" + ["05"] = "Informational", } -- Payload names @@ -69,7 +88,7 @@ local payloads = { ["05"] = "ID", ["08"] = "Hash", ["0A"] = "Nonce", - ["0D"] = "VID" + ["0D"] = "VID", } @@ -113,8 +132,8 @@ end -- local function generate_random(length) local rnd = "" - - for i=1, length do + + for i=1, length do rnd = rnd .. string.format("%.2X", math.random(255)) end return rnd @@ -125,9 +144,8 @@ end -- local function convert_to_hex(id) local hex_str = "" - - for c in string.gmatch(id, ".") do - hex_str = hex_str .. string.format("%X", c:byte()) + for c in string.gmatch(id, ".") do + hex_str = hex_str .. string.format("%X", c:byte()) end return hex_str end @@ -135,13 +153,13 @@ end -- Extract Payloads local function extract_payloads(packet) - + -- packet only contains HDR if packet:len() < 61 then return {} end local np = packet:sub(33,34) -- next payload - local index = 61 -- starting point for search - local ike_headers = {} -- ike headers + local index = 61 -- starting point for search + local ike_headers = {} -- ike headers local payload = '' -- loop over packet @@ -169,7 +187,7 @@ local function extract_payloads(packet) -- jump to the next payload index = index + payload_length end - return ike_headers + return ike_headers end @@ -183,8 +201,8 @@ end -- -- NOTE: the second step currently only has support for CISCO devices -- --- Input is a table of collected vendor-ids, output is a table --- with fields: +-- Input is a table of collected vendor-ids, output is a table +-- with fields: -- vendor, version, name, attributes (table), guess (table), os local function lookup(vendor_ids) if vendor_ids == {} or vendor_ids == nil then return {} end @@ -206,35 +224,34 @@ local function lookup(vendor_ids) -- loop over the vendor_ids returned in ike request for _,vendor_id in pairs(vendor_ids) do - - -- loop over the fingerprints found in database + + -- loop over the fingerprints found in database for _,row in pairs(fingerprints) do if vendor_id:find(row.fingerprint) then -- if a match is found, check if it's a version detection or attribute if row.category == 'vendor' then - + -- Only store the first match if info.vendor == nil then - -- the fingerprint contains information about the VID info.vendor = row local debug_string = '' if row.vendor ~= nil then debug_string = debug_string .. row.vendor .. ' ' end - if row.version ~= nil then debug_string = debug_string .. row.version end + if row.version ~= nil then debug_string = debug_string .. row.version end stdnse.print_debug(2, "IKE: Fingerprint: %s matches %s", vendor_id, debug_string) end - + elseif row.category == 'attribute' then info.attribs[ #info.attribs + 1] = row stdnse.print_debug(2, "IKE: Attribute: %s matches %s", vendor_id, row.text) break end end - end + end end end @@ -243,36 +260,36 @@ local function lookup(vendor_ids) -- Search for the order of the vids -- Uses category 'vid_ordering' --- - + -- search in the 'vid_ordering' category local debug_string = '' for _,row in pairs(fingerprints) do if row.category == 'vid_ordering' and all_vids:find(row.fingerprint) then - + -- Use ordering information if there where no vendor matches from prevoius step if info.vendor == nil then info.vendor = row - + -- Debugging info debug_string = '' if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' ' end if info.vendor.version ~= nil then debug_string = debug_string .. info.vendor.version .. ' ' end if info.vendor.ostype ~= nil then debug_string = debug_string .. info.vendor.ostype end stdnse.print_debug(2, 'IKE: No vendor match, but ordering match found: %s', debug_string) - + return info - + -- Update OS based on ordering elseif info.vendor.vendor == row.vendor then info.vendor.ostype = row.ostype - + -- Debugging info debug_string = '' if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' to ' end if row.ostype ~= nil then debug_string = debug_string .. row.ostype end stdnse.print_debug(2, 'IKE: Vendor and ordering match. OS updated: %s', debug_string) - + return info -- Only print debugging information if conflicting information is detected @@ -282,7 +299,7 @@ local function lookup(vendor_ids) if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' vs ' end if row.vendor ~= nil then debug_string = debug_string .. row.vendor end stdnse.print_debug(2, 'IKE: Found an ordering match, but vendors do not match. %s', debug_string) - + end end end @@ -294,8 +311,8 @@ end -- Handle a response packet -- A very limited response parser -- Currently only the VIDs are extracted --- This could be made more advanced to --- allow for fingerprinting via the order +-- This could be made more advanced to +-- allow for fingerprinting via the order -- of the returned headers --- function response(packet) @@ -308,7 +325,7 @@ function response(packet) local ike_headers = {} -- simple check that the type is something other than 'Informational' - -- as this type does not include VIDs + -- as this type does not include VIDs if resp_type ~= "Informational" then resp["mode"] = resp_type @@ -334,7 +351,7 @@ end -- and is a hex string -- function send_request( host, port, packet ) - + local socket = nmap.new_socket() local s_status, r_status, data, i, hexstring, _ @@ -345,13 +362,13 @@ function send_request( host, port, packet ) s_status,_ = socket:send(packet) -- receive answer - if s_status then + if s_status then r_status, data = socket:receive_lines(1) - + if r_status then i, hexstring = bin.unpack("H" .. data:len(), data) socket:close() - return response(hexstring) + return response(hexstring) else socket:close() end @@ -363,7 +380,7 @@ function send_request( host, port, packet ) end -- Create the aggressive part of a packet --- Aggressive mode includes the user-id, so the +-- Aggressive mode includes the user-id, so the -- length of this has to be taken into account -- local function generate_aggressive(port, protocol, id, diffie) @@ -377,39 +394,38 @@ local function generate_aggressive(port, protocol, id, diffie) key_length = 96 elseif diffie == 2 then key_length = 128 - end - + end return bin.pack(">SHHSSHSHCHHH", -- Key Exchange - 0x0a00 , -- Next payload (Nonce) + 0x0a00 , -- Next payload (Nonce) string.format("%04X", key_length+4) , -- Length (132-bit) - generate_random(key_length) , -- Random key data + generate_random(key_length) , -- Random key data -- Nonce - 0x0500 , -- Next payload (Identification) - 0x0018 , -- Length (24) - generate_random(20) , -- Nonce data + 0x0500 , -- Next payload (Identification) + 0x0018 , -- Length (24) + generate_random(20) , -- Nonce data - -- Identification - 0x0000 , -- Next Payload (None) - id_len , -- Payload length (id + 8) - 0x03 , -- ID Type (USER_FQDN) - hex_prot , -- Protocol ID (UDP) - hex_port , -- Port (500) - convert_to_hex(id) -- Id Data (as hex) + -- Identification + 0x0000 , -- Next Payload (None) + id_len , -- Payload length (id + 8) + 0x03 , -- ID Type (USER_FQDN) + hex_prot , -- Protocol ID (UDP) + hex_port , -- Port (500) + convert_to_hex(id) -- Id Data (as hex) ) end -- Create the transform -- AES encryption needs an extra value to define the key length --- Currently only DES, 3DES and AES encryption is supported +-- Currently only DES, 3DES and AES encryption is supported -- local function generate_transform(auth, encryption, hash, group, number, total) local key_length, trans_length, aes_enc, sep, enc local next_payload, payload_number - + -- handle special case of aes if encryption:sub(1,3) == "aes" then trans_length = 0x0028 @@ -431,25 +447,25 @@ local function generate_transform(auth, encryption, hash, group, number, total) -- set the payload number payload_number = string.format("%.2X", number) - local trans = bin.pack(">SSHCSIIII", + local trans = bin.pack(">SSHCSIIII", next_payload , -- Next payload trans_length , -- Transform length payload_number , -- Transform number - 0x01 , -- Transform ID (IKE) - 0x0000 , -- spacers ? - enc , -- Encryption algorithm + 0x01 , -- Transform ID (IKE) + 0x0000 , -- spacers ? + enc , -- Encryption algorithm hash_algo[hash] , -- Hash algorithm - authentication[auth], -- Authentication method - group_desc[group] -- Group Description + authentication[auth] , -- Authentication method + group_desc[group] -- Group Description ) - + if key_length ~= nil then trans = trans .. bin.pack(">I", key_length) -- only set for aes end trans = trans .. bin.pack(">IL", - 0x800b0001 , -- Life type (seconds) - 0x000c000400007080 -- Life duration (28800) + 0x800b0001 , -- Life type (seconds) + 0x000c000400007080 -- Life duration (28800) ) return trans @@ -457,7 +473,7 @@ end -- Generate multiple transforms --- Input nust be a table of complete transforms +-- Input nust be a table of complete transforms -- local function generate_transforms(transform_table) local transforms = '' @@ -481,9 +497,9 @@ function request(port, proto, mode, transforms, diffie, id) transform_string = generate_transforms(transforms) number_transforms = string.format("%.2X", #transforms) - -- check for aggressive vs Main mode - if mode == "Aggressive" then - str_aggressive = generate_aggressive(port, proto, id, diffie) + -- check for aggressive vs Main mode + if mode == "Aggressive" then + str_aggressive = generate_aggressive(port, proto, id, diffie) payload_after_sa = 0x0400 else str_aggressive = "" @@ -497,29 +513,29 @@ function request(port, proto, mode, transforms, diffie, id) l_pro = string.format("%.4X", 8 + transform_string:len()) -- Build the packet - local packet = bin.pack(">HLCCCCIHSHIISHCCCH", + local packet = bin.pack(">HLCCCCIHSHIISHCCCH", generate_random(8) , -- Initiator cookie 0x0000000000000000 , -- Responder cookie - 0x01 , -- Next payload (SA) - 0x10 , -- Version + 0x01 , -- Next payload (SA) + 0x10 , -- Version exchange_mode[mode] , -- Exchange type - 0x00 , -- Flags - 0x00000000 , -- Message id - l , -- packet length + 0x00 , -- Flags + 0x00000000 , -- Message id + l , -- packet length - --# Security Association + -- Security Association payload_after_sa , -- Next payload (Key exchange, if aggressive mode) - l_sa , -- Length - 0x00000001 , -- IPSEC - 0x00000001 , -- Situation + l_sa , -- Length + 0x00000001 , -- IPSEC + 0x00000001 , -- Situation --## Proposal - 0x0000 , -- Next payload (None) - l_pro , -- Payload length - 0x01 , -- Proposal number - 0x01 , -- Protocol ID (ISAKMP) - 0x00 , -- SPI Size + 0x0000 , -- Next payload (None) + l_pro , -- Payload length + 0x01 , -- Proposal number + 0x01 , -- Protocol ID (ISAKMP) + 0x00 , -- SPI Size number_transforms -- Proposal transforms ) @@ -533,4 +549,4 @@ function request(port, proto, mode, transforms, diffie, id) end -return _ENV \ No newline at end of file +return _ENV