mirror of
https://github.com/nmap/nmap.git
synced 2025-12-15 20:29:03 +00:00
Add the scripts
citrix-brute-xml citrix-enum-apps citrix-enum-apps-xml citrix-enum-servers citrix-enum-servers-xml and the citrixxml modules, all by Patrik Karlsson.
This commit is contained in:
11
CHANGELOG
11
CHANGELOG
@@ -16,6 +16,17 @@ o Added new NSE scripts:
|
||||
system, server build date, and upstream time server IP address.
|
||||
[Richard Sammet]
|
||||
|
||||
o citrix-brute-xml uses the unpwdb library to guess credentials for
|
||||
the Citrix PN Web Agent Service. [Patrik Karlsson]
|
||||
|
||||
o citrix-enum-apps and citrix-enum-apps-xml get a list of published
|
||||
applications from the Citrix ICA Browser or XML service,
|
||||
respectively. [Patrik Karlsson]
|
||||
|
||||
o citrix-enum-servers and citrix-enum-servers-xml.nse get a list of
|
||||
Citrix servers from the Citrix ICA Browser or XML service,
|
||||
respectively. [Patrik Karlsson]
|
||||
|
||||
o Removed a limitation of snmp.lua that only allowed it to properly
|
||||
encode OID component values up to 127. The bug was reported by
|
||||
Victor Rudnev. [David]
|
||||
|
||||
571
nselib/citrixxml.lua
Normal file
571
nselib/citrixxml.lua
Normal file
@@ -0,0 +1,571 @@
|
||||
---
|
||||
-- This module was written by Patrik Karlsson and facilitates communication
|
||||
-- with the Citrix XML Service. It is not feature complete and is missing several
|
||||
-- functions and parameters.
|
||||
--
|
||||
-- The library makes little or no effort to verify that the parameters submitted
|
||||
-- to each function are compliant with the DTD
|
||||
--
|
||||
-- As all functions handling requests take their parameters in the form of tables,
|
||||
-- additional functionality can be added while not breaking existing scripts
|
||||
--
|
||||
-- Details regarding the requests/responses and their parameters can be found in
|
||||
-- the NFuse.DTD included with Citrix MetaFrame/Xenapp
|
||||
--
|
||||
-- This code is based on the information available in:
|
||||
-- NFuse.DTD - Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
|
||||
|
||||
require 'http'
|
||||
|
||||
module(... or "citrix",package.seeall)
|
||||
|
||||
--- Decodes html-entities to chars eg.   => <space>
|
||||
--
|
||||
-- @param str string to convert
|
||||
-- @return string an e
|
||||
function decode_xml_document(xmldata)
|
||||
|
||||
local hexval
|
||||
|
||||
if not xmldata then
|
||||
return ""
|
||||
end
|
||||
|
||||
local newstr = xmldata
|
||||
|
||||
for m in xmldata:gmatch("(\&\#%d+;)") do
|
||||
hexval = m:match("(%d+)")
|
||||
|
||||
if ( hexval ) then
|
||||
newstr = xmldata:gsub(m, string.char(hexval))
|
||||
end
|
||||
end
|
||||
|
||||
return newstr
|
||||
|
||||
end
|
||||
|
||||
--- Sends the request to the server using the http lib
|
||||
--
|
||||
-- NOTE:
|
||||
-- At the time of the development (20091128) the http
|
||||
-- lib does not properly handle text/xml content. It also doesn't
|
||||
-- handle HTTP 100 Continue properly. Workarounds are in place,
|
||||
-- please consult comments.
|
||||
--
|
||||
-- @param host string, the ip of the remote server
|
||||
-- @param port number, the port of the remote server
|
||||
-- @param xmldata string, the HTTP data part of the request as XML
|
||||
--
|
||||
-- @return string with the response body
|
||||
--
|
||||
function send_citrix_xml_request(host, port, xmldata)
|
||||
|
||||
local header = "POST /scripts/WPnBr.dll HTTP/1.1\r\n"
|
||||
header = header .. "Content-type: text/xml\r\n"
|
||||
header = header .. "Host: " .. host .. ":" .. port .. "\r\n"
|
||||
header = header .. "Content-Length: " .. xmldata:len() .. "\r\n"
|
||||
header = header .. "Connection: Close\r\n"
|
||||
header = header .. "\r\n"
|
||||
|
||||
local request = header .. xmldata
|
||||
|
||||
-- this would have been really great! Unfortunately buildPost substitutes all spaces for plus'
|
||||
-- this ain't all great when the content-type is text/xml
|
||||
-- local response = http.post( host, port, "/scripts/WPnBr.dll", { header={["Content-Type"]="text/xml"}}, nil, xmldata)
|
||||
|
||||
-- let's build the content ourselves and let the http module do the rest
|
||||
local response = http.request(host, port, request)
|
||||
local parse_options = {method="post"}
|
||||
|
||||
-- we need to handle another bug within the http module
|
||||
-- it doesn't seem to recognize the HTTP/100 Continue correctly
|
||||
-- So, we need to chop that part of from the response
|
||||
if response and response:match("^HTTP/1.1 100 Continue") and response:match( "\r?\n\r?\n" ) then
|
||||
response = response:match( "\r?\n\r?\n(.*)$" )
|
||||
end
|
||||
|
||||
-- time for next workaround
|
||||
-- The Citrix XML Service returns the header Transfer-Coding, rather than Transfer-Encoding
|
||||
-- Needless to say, this screws things up for the http library
|
||||
if response and response:match("Transfer[-]Coding") then
|
||||
response = response:gsub("Transfer[-]Coding", "Transfer-Encoding")
|
||||
end
|
||||
|
||||
local response = http.parseResult(response, parse_options)
|
||||
|
||||
-- this is *probably* not the right way to do stuff
|
||||
-- decoding should *probably* only be done on XML-values
|
||||
-- this is *probably* defined in the standard, for anyone interested
|
||||
return decode_xml_document(response.body)
|
||||
|
||||
end
|
||||
|
||||
--- Request information about the Citrix Server Farm
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
-- @param socket socket, connected to the remote web server
|
||||
-- @return string HTTP response data
|
||||
--
|
||||
function request_server_farm_data( host, port )
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"1.1\">"
|
||||
xmldata = xmldata .. "<RequestServerFarmData></RequestServerFarmData>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
--- Parses the response from the request_server_farm_data request
|
||||
-- @param response string with the XML response
|
||||
-- @return table containing server farm names
|
||||
--
|
||||
function parse_server_farm_data_response( response )
|
||||
|
||||
local farms = {}
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
for farm in response:gmatch("<ServerFarmName.->([^\<]+)</ServerFarmName>") do
|
||||
table.insert(farms, farm)
|
||||
end
|
||||
|
||||
return farms
|
||||
|
||||
end
|
||||
|
||||
--- Sends a request for application data to the Citrix XML service
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function does NOT implement all the supported parameters
|
||||
--
|
||||
-- Supported parameters are Scope, ServerType, ClientType, DesiredDetails
|
||||
--
|
||||
-- @param host string the host which is to be queried
|
||||
-- @param port number the port number of the XML service
|
||||
-- @param params table with parameters
|
||||
-- @return string HTTP response data
|
||||
--
|
||||
function request_appdata(host, port, params)
|
||||
|
||||
-- setup the mandatory parameters if they're missing
|
||||
local scope = params['Scope'] or "onelevel"
|
||||
local server_type = params['ServerType'] or "all"
|
||||
local client_type = params['ClientType'] or "ica30"
|
||||
local desired_details = params['DesiredDetails'] or nil
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"5.0\">"
|
||||
xmldata = xmldata .. "<RequestAppData>"
|
||||
xmldata = xmldata .. "<Scope traverse=\"" .. scope .. "\" />"
|
||||
xmldata = xmldata .. "<ServerType>" .. server_type .. "</ServerType>"
|
||||
xmldata = xmldata .. "<ClientType>" .. client_type .. "</ClientType>"
|
||||
|
||||
if desired_details then
|
||||
if type(desired_details) == "string" then
|
||||
xmldata = xmldata .. "<DesiredDetails>" .. desired_details .. "</DesiredDetails>"
|
||||
elseif type(desired_details) == "table" then
|
||||
for _, v in ipairs(desired_details) do
|
||||
xmldata = xmldata .. "<DesiredDetails>" .. v .. "</DesiredDetails>"
|
||||
end
|
||||
else
|
||||
assert(desired_details)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</RequestAppData>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
|
||||
--- Extracts the Accesslist section of the XML response
|
||||
--
|
||||
-- @param xmldata string containing results from the request app data request
|
||||
-- @return table containing settings extracted from the accesslist section of the response
|
||||
local function extract_appdata_acls(xmldata)
|
||||
|
||||
local acls = {}
|
||||
local users = {}
|
||||
local groups = {}
|
||||
|
||||
for acl in xmldata:gmatch("<AccessList>(.-)</AccessList>") do
|
||||
|
||||
if acl:match("AnonymousUser") then
|
||||
table.insert(users, "Anonymous")
|
||||
else
|
||||
|
||||
for user in acl:gmatch("<User>(.-)</User>") do
|
||||
local user_name = user:match("<UserName.->(.-)</UserName>") or ""
|
||||
local domain_name = user:match("<Domain.->(.-)</Domain>") or ""
|
||||
|
||||
if user_name:len() > 0 then
|
||||
if domain_name:len() > 0 then
|
||||
domain_name = domain_name .. "\\"
|
||||
end
|
||||
table.insert(users, domain_name .. user_name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
for group in acl:gmatch("<Group>(.-)</Group>") do
|
||||
|
||||
|
||||
local group_name = group:match("<GroupName.->(.-)</GroupName>") or ""
|
||||
local domain_name = group:match("<Domain.->(.-)</Domain>") or ""
|
||||
|
||||
if group_name:len() > 0 then
|
||||
if domain_name:len() > 0 then
|
||||
domain_name = domain_name .. "\\"
|
||||
end
|
||||
table.insert(groups, domain_name .. group_name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if #users> 0 then
|
||||
acls['User'] = users
|
||||
end
|
||||
if #groups>0 then
|
||||
acls['Group'] = groups
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return acls
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Extracts the settings section of the XML response
|
||||
--
|
||||
-- @param xmldata string containing results from the request app data request
|
||||
-- @return table containing settings extracted from the settings section of the response
|
||||
local function extract_appdata_settings(xmldata)
|
||||
|
||||
local settings = {}
|
||||
|
||||
settings['appisdisabled'] = xmldata:match("<Settings.-appisdisabled=\"(.-)\".->")
|
||||
settings['appisdesktop'] = xmldata:match("<Settings.-appisdesktop=\"(.-)\".->")
|
||||
|
||||
for s in xmldata:gmatch("<Settings.->(.-)</Settings>") do
|
||||
settings['Encryption'] = s:match("<Encryption.->(.-)</Encryption>")
|
||||
settings['AppOnDesktop'] = s:match("<AppOnDesktop.-value=\"(.-)\"/>")
|
||||
settings['AppInStartmenu'] = s:match("<AppInStartmenu.-value=\"(.-)\"/>")
|
||||
settings['PublisherName'] = s:match("<PublisherName.->(.-)</PublisherName>")
|
||||
settings['SSLEnabled'] = s:match("<SSLEnabled.->(.-)</SSLEnabled>")
|
||||
settings['RemoteAccessEnabled'] = s:match("<RemoteAccessEnabled.->(.-)</RemoteAccessEnabled>")
|
||||
end
|
||||
|
||||
return settings
|
||||
|
||||
end
|
||||
|
||||
--- Parses the appdata XML response
|
||||
--
|
||||
-- @param xmldata string response from request_appdata
|
||||
-- @return table containing nestled tables closely resembling the DOM model of the XML response
|
||||
function parse_appdata_response(xmldata)
|
||||
|
||||
local apps = {}
|
||||
xmldata = xmldata:gsub("\r?\n",""):gsub(">%s+<", "><")
|
||||
|
||||
for AppData in xmldata:gmatch("<AppData>(.-)</AppData>") do
|
||||
|
||||
local app_name = AppData:match("<FName.->(.-)</FName>") or ""
|
||||
local app = {}
|
||||
|
||||
app['FName'] = app_name
|
||||
app['AccessList'] = extract_appdata_acls(AppData)
|
||||
app['Settings'] = extract_appdata_settings(AppData)
|
||||
|
||||
table.insert(apps, app)
|
||||
|
||||
end
|
||||
|
||||
return apps
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- @param flags string, should be any of following: alt-addr, no-load-bias
|
||||
--
|
||||
function request_address(host, port, flags, appname)
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"4.1\">"
|
||||
xmldata = xmldata .. "<RequestAddress>"
|
||||
|
||||
if flags then
|
||||
xmldata = xmldata .. "<Flags>" .. flags .. "</Flags>"
|
||||
end
|
||||
|
||||
if appname then
|
||||
xmldata = xmldata .. "<Name>"
|
||||
xmldata = xmldata .. "<AppName>" .. appname .. "</AppName>"
|
||||
xmldata = xmldata .. "</Name>"
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</RequestAddress>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
--- Request information about the Citrix protocol
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
-- @param host string the host which is to be queried
|
||||
-- @param port number the port number of the XML service
|
||||
-- @param params table with parameters
|
||||
-- @return string HTTP response data
|
||||
--
|
||||
function request_server_data(host, port, params)
|
||||
|
||||
local params = params or {}
|
||||
local server_type = params.ServerType or {"all"}
|
||||
local client_type = params.ClientType or {"all"}
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"1.1\">"
|
||||
xmldata = xmldata .. "<RequestServerData>"
|
||||
|
||||
for _, srvtype in pairs(server_type) do
|
||||
xmldata = xmldata .. "<ServerType>" .. srvtype .. "</ServerType>"
|
||||
end
|
||||
|
||||
for _, clitype in pairs(client_type) do
|
||||
xmldata = xmldata .. "<ClientType>" .. clitype .. "</ClientType>"
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</RequestServerData>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
--- Parses the response from the request_server_data request
|
||||
-- @param response string with the XML response
|
||||
-- @return table containing the server names
|
||||
--
|
||||
function parse_server_data_response(response)
|
||||
|
||||
local servers = {}
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
for s in response:gmatch("<ServerName>([^\<]+)</ServerName>") do
|
||||
table.insert(servers, s)
|
||||
end
|
||||
|
||||
return servers
|
||||
|
||||
end
|
||||
|
||||
--- Request information about the Citrix protocol
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
-- @param host string the host which is to be queried
|
||||
-- @param port number the port number of the XML service
|
||||
-- @param params table with parameters
|
||||
-- @return string HTTP response data
|
||||
--
|
||||
function request_protocol_info( host, port, params )
|
||||
|
||||
local params = params or {}
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"1.1\">"
|
||||
xmldata = xmldata .. "<RequestProtocolInfo>"
|
||||
|
||||
if params['ServerAddress'] then
|
||||
xmldata = xmldata .. "<ServerAddress addresstype=\"" .. params['ServerAddress']['attr']['addresstype'] .. "\">"
|
||||
xmldata = xmldata .. params['ServerAddress'] .. "</ServerAddress>"
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</RequestProtocolInfo>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
--- Request capability information
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
-- @param host string the host which is to be queried
|
||||
-- @param port number the port number of the XML service
|
||||
-- @param params table with parameters
|
||||
-- @return string HTTP response data
|
||||
--
|
||||
function request_capabilities( host, port )
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"1.1\">"
|
||||
xmldata = xmldata .. "<RequestCapabilities>"
|
||||
xmldata = xmldata .. "</RequestCapabilities>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
--- Parses the response from the request_capabilities request
|
||||
-- @param response string with the XML response
|
||||
-- @return table containing the server capabilities
|
||||
--
|
||||
function parse_capabilities_response(response)
|
||||
|
||||
local servers = {}
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
for s in response:gmatch("<CapabilityId.->([^\<]+)</CapabilityId>") do
|
||||
table.insert(servers, s)
|
||||
end
|
||||
|
||||
return servers
|
||||
|
||||
end
|
||||
|
||||
--- Tries to validate user credentials against the XML service
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
--
|
||||
-- @param host string the host which is to be queried
|
||||
-- @param port number the port number of the XML service
|
||||
-- @param params table with parameters
|
||||
-- @return string HTTP response data
|
||||
--
|
||||
function request_validate_credentials(host, port, params )
|
||||
|
||||
local params = params or {}
|
||||
local credentials = params['Credentials'] or {}
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"5.0\">"
|
||||
xmldata = xmldata .. "<RequestValidateCredentials>"
|
||||
xmldata = xmldata .. "<Credentials>"
|
||||
|
||||
if credentials['UserName'] then
|
||||
xmldata = xmldata .. "<UserName>" .. credentials['UserName'] .. "</UserName>"
|
||||
end
|
||||
|
||||
if credentials['Password'] then
|
||||
xmldata = xmldata .. "<Password encoding=\"cleartext\">" .. credentials['Password'] .. "</Password>"
|
||||
end
|
||||
|
||||
if credentials['Domain'] then
|
||||
xmldata = xmldata .. "<Domain type=\"NT\">" .. credentials['Domain'] .. "</Domain>"
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</Credentials>"
|
||||
xmldata = xmldata .. "</RequestValidateCredentials>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Parses the response from request_validate_credentials
|
||||
-- @param response string with the XML response
|
||||
-- @return table containing the results
|
||||
--
|
||||
function parse_validate_credentials_response(response)
|
||||
local tblResult = {}
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
tblResult['DaysUntilPasswordExpiry'] = response:match("<DaysUntilPasswordExpiry>(.+)</DaysUntilPasswordExpiry>")
|
||||
tblResult['ShowPasswordExpiryWarning'] = response:match("<ShowPasswordExpiryWarning>(.+)</ShowPasswordExpiryWarning>")
|
||||
tblResult['ErrorId'] = response:match("<ErrorId>(.+)</ErrorId>")
|
||||
|
||||
return tblResult
|
||||
|
||||
end
|
||||
|
||||
--- Sends a request to reconnect session data
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function does NOT implement all the supported parameters
|
||||
----
|
||||
-- @param host string the host which is to be queried
|
||||
-- @param port number the port number of the XML service
|
||||
-- @param params table with parameters
|
||||
--
|
||||
function request_reconnect_session_data(host, port, params)
|
||||
|
||||
local params = params or {}
|
||||
local Credentials = params.Credentials or {}
|
||||
|
||||
params.ServerType = params.ServerType or {}
|
||||
params.ClientType = params.ClientType or {}
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"5.0\">"
|
||||
xmldata = xmldata .. "<RequestReconnectSessionData>"
|
||||
|
||||
xmldata = xmldata .. "<Credentials>"
|
||||
|
||||
if Credentials.UserName then
|
||||
xmldata = xmldata .. "<UserName>" .. Credentials.UserName .. "</UserName>"
|
||||
end
|
||||
|
||||
if Credentials.Password then
|
||||
xmldata = xmldata .. "<Password encoding=\"cleartext\">" .. Credentials.Password .. "</Password>"
|
||||
end
|
||||
|
||||
if Credentials.Domain then
|
||||
xmldata = xmldata .. "<Domain type=\"NT\">" .. Credentials.Domain .. "</Domain>"
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</Credentials>"
|
||||
|
||||
if params.ClientName then
|
||||
xmldata = xmldata .. "<ClientName>" .. params.ClientName .. "</ClientName>"
|
||||
end
|
||||
|
||||
if params.DeviceId then
|
||||
xmldata = xmldata .. "<DeviceId>" .. params.DeviceId .. "</DeviceId>"
|
||||
end
|
||||
|
||||
for _, srvtype in pairs(params.ServerType) do
|
||||
xmldata = xmldata .. "<ServerType>" .. srvtype .. "</ServerType>"
|
||||
end
|
||||
|
||||
for _, clitype in pairs(params.ClientType) do
|
||||
xmldata = xmldata .. "<ClientType>" .. clitype .. "</ClientType>"
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</RequestReconnectSessionData>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
|
||||
|
||||
end
|
||||
160
scripts/citrix-brute-xml.nse
Normal file
160
scripts/citrix-brute-xml.nse
Normal file
@@ -0,0 +1,160 @@
|
||||
description = [[ Attempts to guess valid credentials for the Citrix PN Web Agent XML Service.
|
||||
The XML service authenticates against the local Windows server or the Active Directory.
|
||||
|
||||
CAUTION: This script makes no attempt of preventing account lockout.
|
||||
If the password list contains more passwords than the lockout-threshold
|
||||
accounts WILL be locked.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script=citrix-brute-xml --script-args=userdb=<userdb>,passdb=<passdb>,ntdomain=<domain> -p 80,443,8080 <host>
|
||||
--
|
||||
-- @output
|
||||
-- PORT STATE SERVICE REASON
|
||||
-- 8080/tcp open http-proxy syn-ack
|
||||
-- | citrix-brute-xml:
|
||||
-- | Joe:password => Must change password at next logon
|
||||
-- | Luke:summer => Login was successful
|
||||
-- |_ Jane:secret => Account is disabled
|
||||
--
|
||||
---
|
||||
|
||||
-- Version 0.2
|
||||
|
||||
-- Created 11/30/2009 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 12/02/2009 - v0.2 - Use stdnse.format_ouput for output
|
||||
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"intrusive", "auth"}
|
||||
|
||||
require 'unpwdb'
|
||||
require 'shortport'
|
||||
require 'citrixxml'
|
||||
|
||||
portrule = shortport.portnumber({8080,80,443}, "tcp")
|
||||
|
||||
--- Verifies if the credentials (username, password and domain) are valid
|
||||
--
|
||||
-- @param host string, the ip against which to perform
|
||||
-- @param port number, the port number of the XML service
|
||||
-- @param username string, the username to authenticate as
|
||||
-- @param password string, the password to authenticate with
|
||||
-- @param domain string, the Windows domain to authenticate against
|
||||
--
|
||||
-- @return success, message
|
||||
--
|
||||
function verify_password( host, port, username, password, domain )
|
||||
|
||||
local response = citrixxml.request_validate_credentials(host, port, {Credentials={Domain=domain, Password=password, UserName=username}})
|
||||
local cred_status = citrixxml.parse_validate_credentials_response(response)
|
||||
|
||||
local account = {}
|
||||
|
||||
account.username = username
|
||||
account.password = password
|
||||
account.domain = domain
|
||||
|
||||
if cred_status.ErrorId then
|
||||
if cred_status.ErrorId == "must-change-credentials" then
|
||||
account.valid = true
|
||||
account.message = "Must change password at next logon"
|
||||
elseif cred_status.ErrorId == "account-disabled" then
|
||||
account.valid = true
|
||||
account.message = "Account is disabled"
|
||||
elseif cred_status.ErrorId == "account-locked-out" then
|
||||
account.valid = false
|
||||
account.message = "Account Locked Out"
|
||||
elseif cred_status.ErrorId == "failed-credentials" then
|
||||
account.valid = false
|
||||
account.message = "Incorrect Password"
|
||||
elseif cred_status.ErrorId == "unspecified" then
|
||||
account.valid = false
|
||||
account.message = "Unspecified"
|
||||
else
|
||||
print("UNKNOWN response: " .. response)
|
||||
account.valid = false
|
||||
account.message = "failed"
|
||||
end
|
||||
else
|
||||
account.message = "Login was successful"
|
||||
account.valid = true
|
||||
end
|
||||
|
||||
return account
|
||||
|
||||
end
|
||||
|
||||
--- Formats the result from the table of valid accounts
|
||||
--
|
||||
-- @param accounts table containing accounts (tables)
|
||||
-- @return string containing the result
|
||||
function create_result_from_table(accounts)
|
||||
|
||||
local result = ""
|
||||
|
||||
for _, account in ipairs(accounts) do
|
||||
result = result .. " " .. account.username .. ":" .. account.password .. " => " .. account.message .. "\n"
|
||||
end
|
||||
|
||||
return " \n" .. result
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local status, nextUser, nextPass
|
||||
local username, password
|
||||
local args = nmap.registry.args
|
||||
local ntdomain = args.ntdomain
|
||||
local valid_accounts = {}
|
||||
|
||||
if not ntdomain then
|
||||
return "FAILED: No domain specified (use ntdomain argument)"
|
||||
end
|
||||
|
||||
status, nextUser = unpwdb.usernames()
|
||||
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
|
||||
status, nextPass = unpwdb.passwords()
|
||||
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
|
||||
username = nextUser()
|
||||
|
||||
-- iterate over userlist
|
||||
while username do
|
||||
password = nextPass()
|
||||
|
||||
-- iterate over passwordlist
|
||||
while password do
|
||||
local result = "Trying " .. username .. "/" .. password .. " "
|
||||
local account = verify_password(host.ip, port.number, username, password, ntdomain)
|
||||
|
||||
if account.valid then
|
||||
|
||||
table.insert(valid_accounts, account)
|
||||
|
||||
if account.valid then
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Correct, Info: %s", username, password, account.message)
|
||||
else
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Correct", username, password)
|
||||
end
|
||||
else
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Failed, Reason: %s", username, password, account.message)
|
||||
end
|
||||
password = nextPass()
|
||||
end
|
||||
|
||||
nextPass("reset")
|
||||
username = nextUser()
|
||||
end
|
||||
|
||||
return create_result_from_table(valid_accounts)
|
||||
end
|
||||
150
scripts/citrix-enum-apps-xml.nse
Normal file
150
scripts/citrix-enum-apps-xml.nse
Normal file
@@ -0,0 +1,150 @@
|
||||
description = [[
|
||||
Extracts a list of applications, acls and settings from Citrix XML service
|
||||
|
||||
The script returns the shorter, comma separated output per default.
|
||||
Running nmap with the verbose flag (-v) triggers the detailed output.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script=citrix-enum-apps-xml -p 80,443,8080 <host>
|
||||
--
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- 8080/tcp open http-proxy
|
||||
-- | citrix-enum-apps-xml:
|
||||
-- | Application: Notepad
|
||||
-- | Disabled: false
|
||||
-- | Desktop: false
|
||||
-- | On Desktop: false
|
||||
-- | Encryption: basic
|
||||
-- | In start menu: false
|
||||
-- | Publisher: labb1farm
|
||||
-- | SSL: false
|
||||
-- | Remote Access: false
|
||||
-- | Users: Anonymous
|
||||
-- | Application: iexplorer
|
||||
-- | Disabled: false
|
||||
-- | Desktop: false
|
||||
-- | On Desktop: false
|
||||
-- | Encryption: basic
|
||||
-- | In start menu: false
|
||||
-- | Publisher: labb1farm
|
||||
-- | SSL: false
|
||||
-- | Remote Access: false
|
||||
-- | Users: Anonymous
|
||||
-- | Application: registry editor
|
||||
-- | Disabled: false
|
||||
-- | Desktop: false
|
||||
-- | On Desktop: false
|
||||
-- | Encryption: basic
|
||||
-- | In start menu: false
|
||||
-- | Publisher: labb1farm
|
||||
-- | SSL: false
|
||||
-- | Remote Access: false
|
||||
-- | Users: WIN-B4RL0SUCJ29\Joe
|
||||
-- |_ Groups: WIN-B4RL0SUCJ29\HR, *CITRIX_BUILTIN*\*CITRIX_ADMINISTRATORS*
|
||||
--
|
||||
--
|
||||
-- PORT STATE SERVICE
|
||||
-- 8080/tcp open http-proxy
|
||||
-- | citrix-enum-apps-xml:
|
||||
-- | Application: Notepad; Users: Anonymous
|
||||
-- | Application: iexplorer; Users: Anonymous
|
||||
-- |_ Application: registry editor; Users: WIN-B4RL0SUCJ29\Joe; Groups: WIN-B4RL0SUCJ29\HR, *CITRIX_BUILTIN*\*CITRIX_ADMINISTRATORS*
|
||||
--
|
||||
---
|
||||
|
||||
-- Version 0.2
|
||||
-- Created 11/26/2009 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 12/02/2009 - v0.2 - Use stdnse.format_ouput for output
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require "comm"
|
||||
require 'shortport'
|
||||
require 'citrixxml'
|
||||
|
||||
portrule = shortport.portnumber({8080,80,443}, "tcp")
|
||||
|
||||
--- Creates a table which is suitable for use with stdnse.format_output
|
||||
--
|
||||
-- @param appdata table with results from parse_appdata_response
|
||||
-- @param mode string short or long, see usage above
|
||||
-- @return table suitable for stdnse.format_output
|
||||
function format_output(appdata, mode)
|
||||
|
||||
local result = {}
|
||||
local setting_titles = { {appisdisabled="Disabled"}, {appisdesktop="Desktop"}, {AppOnDesktop="On Desktop"},
|
||||
{Encryption="Encryption"}, {AppInStartmenu="In start menu"},
|
||||
{PublisherName="Publisher"}, {SSLEnabled="SSL"}, {RemoteAccessEnabled="Remote Access"} }
|
||||
|
||||
|
||||
if mode == "short" then
|
||||
for app_name, AppData in ipairs(appdata) do
|
||||
local line = "Application: " .. AppData.FName
|
||||
|
||||
if AppData.AccessList then
|
||||
|
||||
if AppData.AccessList.User then
|
||||
line = line .. "; Users: " .. stdnse.strjoin(", ", AppData.AccessList.User)
|
||||
end
|
||||
|
||||
if AppData.AccessList.Group then
|
||||
line = line .. "; Groups: " .. stdnse.strjoin(", ", AppData.AccessList.Group)
|
||||
end
|
||||
|
||||
table.insert(result, line)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
for app_name, AppData in ipairs(appdata) do
|
||||
local result_part = {}
|
||||
|
||||
result_part.name = "Application: " .. AppData.FName
|
||||
|
||||
local settings = AppData.Settings
|
||||
|
||||
for _, setting_pairs in ipairs(setting_titles) do
|
||||
for setting_key, setting_title in pairs(setting_pairs) do
|
||||
local setting_value = settings[setting_key] and settings[setting_key] or ""
|
||||
table.insert(result_part, setting_title .. ": " .. setting_value )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if AppData.AccessList then
|
||||
if AppData.AccessList.User then
|
||||
table.insert(result_part, "Users: " .. stdnse.strjoin(", ", AppData.AccessList.User) )
|
||||
end
|
||||
|
||||
if AppData.AccessList.Group then
|
||||
table.insert(result_part, "Groups: " .. stdnse.strjoin(", ", AppData.AccessList.Group) )
|
||||
end
|
||||
|
||||
table.insert(result, result_part)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return result
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function(host,port)
|
||||
|
||||
local response = citrixxml.request_appdata(host.ip, port.number, {ServerAddress="",attr={addresstype="dot"},DesiredDetails={"all","access-list"} })
|
||||
local appdata = citrixxml.parse_appdata_response(response)
|
||||
|
||||
local response = format_output(appdata, (nmap.verbosity() > 1 and "long" or "short"))
|
||||
|
||||
return stdnse.format_output(true, response)
|
||||
|
||||
end
|
||||
157
scripts/citrix-enum-apps.nse
Normal file
157
scripts/citrix-enum-apps.nse
Normal file
@@ -0,0 +1,157 @@
|
||||
description = [[
|
||||
Extract 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 <patrik@cqure.net>"
|
||||
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
|
||||
categories = {"discovery","intrusive"}
|
||||
|
||||
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.ip, port.number, port.protocol) )
|
||||
|
||||
-- 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
|
||||
45
scripts/citrix-enum-servers-xml.nse
Normal file
45
scripts/citrix-enum-servers-xml.nse
Normal file
@@ -0,0 +1,45 @@
|
||||
description = [[ Extracts the name of the server farm and member severs from Citrix XML service
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script=citrix-enum-servers-xml -p 80,443,8080 <host>
|
||||
--
|
||||
-- @output
|
||||
-- PORT STATE SERVICE REASON
|
||||
-- 8080/tcp open http-proxy syn-ack
|
||||
-- | citrix-enum-servers-xml:
|
||||
-- | CITRIX-SRV01
|
||||
-- |_ CITRIX-SRV01
|
||||
--
|
||||
---
|
||||
|
||||
-- Version 0.2
|
||||
|
||||
-- Created 11/26/2009 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 12/02/2009 - v0.2 - Use stdnse.format_ouput for output
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require "comm"
|
||||
require 'shortport'
|
||||
require 'citrixxml'
|
||||
|
||||
portrule = shortport.portnumber({8080,80,443}, "tcp")
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local xmldata = citrixxml.request_server_data(host.ip, port.number)
|
||||
local servers = citrixxml.parse_server_data_response(xmldata)
|
||||
local response = {}
|
||||
|
||||
for _, srv in ipairs(servers) do
|
||||
table.insert(response, srv)
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, response)
|
||||
|
||||
end
|
||||
141
scripts/citrix-enum-servers.nse
Normal file
141
scripts/citrix-enum-servers.nse
Normal file
@@ -0,0 +1,141 @@
|
||||
description = [[
|
||||
Extract a list of Citrix servers from the ICA Browser service
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage sudo ./nmap -sU --script=citrix-enum-servers -p 1604
|
||||
--
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- 1604/udp open unknown
|
||||
-- | citrix-enum-servers:
|
||||
-- | CITRIXSRV01
|
||||
-- |_ CITRIXSRV02
|
||||
--
|
||||
|
||||
-- Version 0.2
|
||||
|
||||
-- Created 11/26/2009 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 11/26/2009 - v0.2 - minor packet documentation
|
||||
|
||||
|
||||
author = "Patrik Karlsson <patrik@cqure.net>"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require "comm"
|
||||
require "shortport"
|
||||
|
||||
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_server_response(response)
|
||||
|
||||
local pos, packet_len = bin.unpack("SS", response)
|
||||
local server_name
|
||||
local server_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, server_name = bin.unpack("z", response:sub(offset))
|
||||
offset = offset + pos - 1
|
||||
table.insert(server_list, server_name)
|
||||
end
|
||||
|
||||
return server_list
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local packet, counter, socket
|
||||
local query = {}
|
||||
local server_list = {}
|
||||
|
||||
--
|
||||
-- Packets were intercepted from the Citrix Program Neighborhood client
|
||||
-- They are used to query a server for it's list of published applications
|
||||
--
|
||||
-- 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(
|
||||
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, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
)
|
||||
|
||||
counter = 0
|
||||
|
||||
socket = nmap.new_socket()
|
||||
socket:set_timeout(5000)
|
||||
|
||||
try = nmap.new_try(function() socket:close() end)
|
||||
try(socket:connect(host.ip, port.number, port.protocol))
|
||||
|
||||
-- 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
|
||||
server_list = process_server_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_server_response( packet )
|
||||
|
||||
for _, v in ipairs(tmp_table) do
|
||||
table.insert(server_list, v)
|
||||
end
|
||||
end
|
||||
|
||||
if #server_list>0 then
|
||||
nmap.set_port_state(host, port, "open")
|
||||
end
|
||||
|
||||
socket:close()
|
||||
|
||||
return stdnse.format_output(true, server_list)
|
||||
|
||||
end
|
||||
@@ -2,6 +2,11 @@ Entry { filename = "asn-query.nse", categories = { "discovery", "external", "saf
|
||||
Entry { filename = "auth-owners.nse", categories = { "default", "safe", } }
|
||||
Entry { filename = "auth-spoof.nse", categories = { "malware", "safe", } }
|
||||
Entry { filename = "banner.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "citrix-brute-xml.nse", categories = { "auth", "intrusive", } }
|
||||
Entry { filename = "citrix-enum-apps-xml.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "citrix-enum-apps.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "citrix-enum-servers-xml.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "citrix-enum-servers.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "daytime.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "db2-info.nse", categories = { "discovery", "safe", "version", } }
|
||||
Entry { filename = "dhcp-discover.nse", categories = { "default", "discovery", "intrusive", } }
|
||||
|
||||
Reference in New Issue
Block a user