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
120
121
122
123
124
125
126
127
128
129
130
131
132
|
local coroutine = require "coroutine"
local nmap = require "nmap"
local os = require "os"
local stdnse = require "stdnse"
local table = require "table"
description = [[
Discovers PC-DUO remote control hosts and gateways running on a LAN by sending a special broadcast UDP probe.
]]
---
-- @usage
-- nmap --script broadcast-pc-duo
--
-- @output
-- Pre-scan script results:
-- | broadcast-pc-duo:
-- | PC-Duo Gateway Server
-- | 10.0.200.113 - WIN2K3SRV-1
-- | PC-Duo Hosts
-- |_ 10.0.200.113 - WIN2K3SRV-1
--
-- @args broadcast-pc-duo.timeout specifies the amount of seconds to sniff
-- the network interface. (default varies according to timing. -T3 = 5s)
author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = { "broadcast", "safe" }
local TIMEOUT = stdnse.parse_timespec(stdnse.get_script_args("broadcast-pc-duo.timeout"))
prerule = function() return ( nmap.address_family() == "inet") end
-- Sends a UDP probe to the server and processes the response
-- @param probe table containing a pc-duo probe
-- @param responses table containing the responses
local function udpProbe(probe, responses)
local condvar = nmap.condvar(responses)
local socket = nmap.new_socket("udp")
socket:set_timeout(500)
for i=1,2 do
local status = socket:sendto(probe.host, probe.port, probe.data)
if ( not(status) ) then
return stdnse.format_output(false, "Failed to send broadcast request")
end
end
local timeout = TIMEOUT or ( 20 / ( nmap.timing_level() + 1 ) )
local stime = os.time()
local hosts = {}
repeat
local status, data = socket:receive()
if ( status ) then
local srvname = data:match(probe.match)
if ( srvname ) then
local status, _, _, rhost, _ = socket:get_info()
if ( not(status) ) then
socket:close()
return false, "Failed to get socket information"
end
-- avoid duplicates
hosts[rhost] = srvname
end
end
until( os.time() - stime > timeout )
socket:close()
local result = {}
for ip, name in pairs(hosts) do
table.insert(result, ("%s - %s"):format(ip,name))
end
if ( #result > 0 ) then
result.name = probe.topic
table.insert(responses, result)
end
condvar "signal"
end
action = function()
-- PC-Duo UDP probes
local probes = {
-- PC-Duo Host probe
{
host = { ip = "255.255.255.255" },
port = { number = 1505, protocol = "udp" },
data = stdnse.fromhex("00808008ff00"),
match= "^.........(%w*)\0",
topic= "PC-Duo Hosts"
},
-- PC-Duo Gateway Server probe
{
host = { ip = "255.255.255.255" },
port = { number = 2303, protocol = "udp" },
data = stdnse.fromhex("20908008ff00"),
match= "^.........(%w*)\0",
topic= "PC-Duo Gateway Server"
},
}
local threads, responses = {}, {}
local condvar = nmap.condvar(responses)
-- start a thread for each probe
for _, p in ipairs(probes) do
local th = stdnse.new_thread( udpProbe, p, responses )
threads[th] = true
end
-- wait until the probes are all done
repeat
for thread in pairs(threads) do
if coroutine.status(thread) == "dead" then
threads[thread] = nil
end
end
if ( next(threads) ) then
condvar "wait"
end
until next(threads) == nil
table.sort(responses, function(a,b) return a.name < b.name end)
-- did we get any responses
if ( #responses > 0 ) then
return stdnse.format_output(true, responses)
end
end
|