summaryrefslogtreecommitdiffstats
path: root/scripts/citrix-enum-apps.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/citrix-enum-apps.nse')
-rw-r--r--scripts/citrix-enum-apps.nse159
1 files changed, 159 insertions, 0 deletions
diff --git a/scripts/citrix-enum-apps.nse b/scripts/citrix-enum-apps.nse
new file mode 100644
index 0000000..35063ae
--- /dev/null
+++ b/scripts/citrix-enum-apps.nse
@@ -0,0 +1,159 @@
+local nmap = require "nmap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local string = require "string"
+local table = require "table"
+
+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 https://nmap.org/book/man-legal.html"
+
+categories = {"discovery","safe"}
+
+
+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 packet_len, pos = string.unpack("<I2", response)
+ local app_name
+ local pa_list = {}
+
+ if packet_len < 40 then
+ return
+ end
+
+ -- the list of published applications starts at offset 40
+ local offset = 41
+
+ while offset < packet_len do
+ app_name, pos = string.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 its 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)
+
+ local 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) ~= "\x01" 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