description = [[
Extracts a list of published applications from the ICA Browser service.
]]
---
-- @usage sudo ./nmap -sU --script=citrix-enum-apps -p 1604 <host>
--
-- @output
-- PORT STATE SERVICE
-- 1604/udp open unknown
-- 1604/udp open unknown
-- | citrix-enum-apps:
-- | Notepad
-- | iexplorer
-- |_ registry editor
--
-- Version 0.2
-- Created 11/24/2009 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
-- Revised 11/25/2009 - v0.2 - fixed multiple packet response bug
author = "Patrik Karlsson"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery","safe"}
require "comm"
require "shortport"
require "stdnse"
require "bin"
portrule = shortport.portnumber(1604, "udp")
-- process the response from the server
-- @param response string, complete server response
-- @return string row delimited with \n containing all published applications
function process_pa_response(response)
local pos, packet_len = bin.unpack("SS", response)
local app_name
local pa_list = {}
if packet_len < 40 then
return
end
-- the list of published applications starts at offset 40
offset = 41
while offset < packet_len do
pos, app_name = bin.unpack("z", response:sub(offset))
offset = offset + pos - 1
table.insert(pa_list, app_name)
end
return pa_list
end
action = function(host, port)
local packet, counter
local query = {}
local pa_list = {}
--
-- Packets were intercepted from the Citrix Program Neighborhood client
-- They are used to query a server for it's list of servers
--
-- We're really not interested in the responses to the first two packets
-- The third response contains the list of published applications
-- I couldn't find any documentation on this protocol so I'm providing
-- some brief information for the bits and bytes this script uses.
--
-- Spec. of response to query[2] that contains a list of published apps
--
-- offset size content
-- -------------------------
-- 0 16-bit Length
-- 12 32-bit Server IP (not used here)
-- 30 8-bit Last packet (1), More packets(0)
-- 40 - null-separated list of applications
--
query[0] = string.char(
0x1e, 0x00, -- Length: 30
0x01, 0x30, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
)
query[1] = string.char(
0x20, 0x00, -- Length: 32
0x01, 0x36, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
)
query[2] = string.char(
0x2a, 0x00, -- Length: 42
0x01, 0x32, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x21, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
)
counter = 0
local socket = nmap.new_socket()
socket:set_timeout(5000)
try = nmap.new_try(function() socket:close() end)
try( socket:connect(host, port) )
-- send the two first packets and never look back
repeat
try( socket:send(query[counter]) )
packet = try(socket:receive())
counter = counter + 1
until (counter>#query)
-- process the first response
pa_list = process_pa_response( packet )
--
-- the byte at offset 31 in the response has a really magic function
-- if it is set to zero (0) we have more response packets to process
-- if it is set to one (1) we have arrived at the last packet of our journey
--
while packet:sub(31,31) ~= string.char(0x01) do
packet = try( socket:receive() )
local tmp_table = process_pa_response( packet )
for _,v in pairs(tmp_table) do
table.insert(pa_list, v)
end
end
-- set port to open
if #pa_list>0 then
nmap.set_port_state(host, port, "open")
end
socket:close()
return stdnse.format_output(true, pa_list)
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez