1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
local nmap = require "nmap"
local match = require "match"
local shortport = require "shortport"
local stdnse = require "stdnse"
local stringaux = require "stringaux"
local table = require "table"
description = [[
Tests a list of known ICAP service names and prints information about
any it detects. The Internet Content Adaptation Protocol (ICAP) is
used to extend transparent proxy servers and is generally used for
content filtering and antivirus scanning.
]]
---
-- @usage
-- nmap -p 1344 <ip> --script icap-info
--
-- @output
-- PORT STATE SERVICE
-- 1344/tcp open unknown
-- | icap-info:
-- | /avscan
-- | Service: C-ICAP/0.1.6 server - Clamav/Antivirus service
-- | ISTag: CI0001-000-0973-6314940
-- | /echo
-- | Service: C-ICAP/0.1.6 server - Echo demo service
-- | ISTag: CI0001-XXXXXXXXX
-- | /srv_clamav
-- | Service: C-ICAP/0.1.6 server - Clamav/Antivirus service
-- | ISTag: CI0001-000-0973-6314940
-- | /url_check
-- | Service: C-ICAP/0.1.6 server - Url_Check demo service
-- |_ ISTag: CI0001-XXXXXXXXX
--
--
author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"safe", "discovery"}
portrule = shortport.port_or_service(1344, "icap")
local function fail(err) return stdnse.format_output(false, err) end
local function parseResponse(resp)
if ( not(resp) ) then
return
end
local resp_p = { header = {}, rawheader = {} }
local resp_tbl = stringaux.strsplit("\r?\n", resp)
if ( not(resp_tbl) or #resp_tbl == 0 ) then
stdnse.debug2("Received an invalid response from server")
return
end
resp_p.status = tonumber(resp_tbl[1]:match("^ICAP/1%.0 (%d*) .*$"))
resp_p['status-line'] = resp_tbl[1]
for i=2, #resp_tbl do
local key, val = resp_tbl[i]:match("^([^:]*):%s*(.*)$")
if ( not(key) or not(val) ) then
stdnse.debug2("Failed to parse header: %s", resp_tbl[i])
else
resp_p.header[key:lower()] = val
end
table.insert(resp_p.rawheader, resp_tbl[i])
end
return resp_p
end
action = function(host, port)
local services = {"/avscan", "/echo", "/srv_clamav", "/url_check", "/nmap" }
local headers = {"Service", "ISTag"}
local probe = {
"OPTIONS icap://%s%s ICAP/1.0",
"Host: %s",
"User-Agent: nmap icap-client/0.01",
"Encapsulated: null-body=0"
}
local hostname = stdnse.get_hostname(host)
local result = {}
for _, service in ipairs(services) do
local socket = nmap.new_socket()
socket:set_timeout(5000)
if ( not(socket:connect(host, port)) ) then
return fail("Failed to connect to server")
end
local request = (table.concat(probe, "\r\n") .. "\r\n\r\n"):format(hostname, service, hostname)
if ( not(socket:send(request)) ) then
socket:close()
return fail("Failed to send request to server")
end
local status, resp = socket:receive_buf(match.pattern_limit("\r\n\r\n", 2048), false)
if ( not(status) ) then
return fail("Failed to receive response from server")
end
local resp_p = parseResponse(resp)
if ( resp_p and resp_p.status == 200 ) then
local result_part = { name = service }
for _, h in ipairs(headers) do
if ( resp_p.header[h:lower()] ) then
table.insert(result_part, ("%s: %s"):format(h, resp_p.header[h:lower()]))
end
end
table.insert(result, result_part)
end
end
return stdnse.format_output(true, result)
end
|