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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
local nmap = require "nmap"
local shortport = require "shortport"
local sip = require "sip"
local stdnse = require "stdnse"
description = [[
Spoofs a call to a SIP phone and detects the action taken by the target (busy, declined, hung up, etc.)
This works by sending a fake sip invite request to the target phone and checking
the responses. A response with status code 180 means that the phone is ringing.
The script waits for the next responses until timeout is reached or a special
response is received. Special responses include: Busy (486), Decline (603),
Timeout (408) or Hang up (200).
]]
---
--@args sip-call-spoof.ua Source application's user agent. Defaults to
-- <code>Ekiga</code>.
--
--@args sip-call-spoof.from Caller user ID. Defaults to <code>Home</code>.
--
--@args sip-call-spoof.extension SIP Extension to send request from. Defaults to
-- <code>100</code>.
--
--@args sip-call-spoof.src Source address to spoof.
--
--@args sip-call-spoof.timeout Time to wait for a response. Defaults to
-- <code>5s</code>
--
-- @usage
-- nmap --script=sip-call-spoof -sU -p 5060 <targets>
-- nmap --script=sip-call-spoof -sU -p 5060 --script-args
-- 'sip-call-spoof.ua=Nmap, sip-call-spoof.from=Boss' <targets>
--
--@output
-- 5060/udp open sip
-- | sip-call-spoof:
-- |_ Target hung up. (After 10.9 seconds)
author = "Hani Benhabiles"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "intrusive"}
portrule = shortport.port_or_service(5060, "sip", {"tcp", "udp"})
--- Function that sends an invite request with given parameters.
-- @arg session SIP Session to use.
-- @arg ua User Agent to use.
-- @arg from SIP From field.
-- @arg src Request source address to spoof.
-- @arg extension Request SIP extension.
-- @return status True if we got a response, false else.
-- @return resp Response table if status is true, error string else.
local sendinvite = function(session, ua, from, src, extension)
local request = sip.Request:new(sip.Method.INVITE)
request:setUri("sip:" .. session.sessdata:getServer())
request:setUA(ua)
if src then
session.sessdata:setDomain(src)
end
session.sessdata:setUsername(extension)
session.sessdata:setName(from)
request:setSessionData(session.sessdata)
return session:exch(request)
end
--- Function that waits for certain responses for an amount of time.
-- @arg session SIP Session to use.
-- @arg timeout Max time to wait for responses other than ringing.
-- @return ringing True if we got a ringing response, false else.
-- @return responsecode Code for the latest meaningful response.
-- could be 180, 200, 486, 408 or 603
local waitresponses = function(session,timeout)
local response, status, data, responsecode, ringing, waittime
local start = nmap.clock_ms()
while (nmap.clock_ms() - start) < timeout do
status, data = session.conn:recv()
if status then
response = sip.Response:new(data)
responsecode = response:getErrorCode()
waittime = nmap.clock_ms() - start
if responsecode == sip.Error.RING then
ringing = true
elseif responsecode == sip.Error.BUSY then
return ringing, sip.Error.BUSY
elseif responsecode == sip.Error.DECLINE then
return ringing, sip.Error.DECLINE, waittime
elseif responsecode == sip.Error.OK then
return ringing, sip.Error.OK, waittime
elseif responsecode == sip.Error.TIMEOUT then
return ringing, sip.Error.OK
end
end
end
if ringing then
return ringing, sip.Error.RING
end
end
--- Function that spoofs an invite request and listens for responses.
-- @arg session SIP Session to use.
-- @arg ua User Agent to use.
-- @arg from SIP From field.
-- @arg src Request source address to spoof.
-- @arg extension Request SIP extension.
-- @arg timeout Max time to wait for responses other than ringing.
-- @return ringing True if we got a ringing response, false else.
-- @return responsecode Code for the latest meaningful response.
-- could be 180, 200, 486, 408 or 603
local invitespoof = function(session, ua, from, src, extension, timeout)
local status, response = sendinvite(session, ua, from, src, extension)
-- check if we got a 100 Trying response.
if status and response:getErrorCode() == 100 then
-- wait for responses
return waitresponses(session, timeout)
end
end
action = function(host, port)
local status, session
local ua = stdnse.get_script_args(SCRIPT_NAME .. ".ua") or "Ekiga"
local from = stdnse.get_script_args(SCRIPT_NAME .. ".from") or "Home"
local src = stdnse.get_script_args(SCRIPT_NAME .. ".src")
local extension = stdnse.get_script_args(SCRIPT_NAME .. ".extension") or 100
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
-- Default timeout value = 5 seconds.
timeout = (timeout or 5) * 1000
session = sip.Session:new(host, port)
status = session:connect()
if not status then
return stdnse.format_output(false, "Failed to connect to the SIP server.")
end
local ringing, result, waittime = invitespoof(session, ua, from, src, extension, timeout)
-- If we get a response, we set the port to open.
if result then
if nmap.get_port_state(host, port) ~= "open" then
nmap.set_port_state(host, port, "open")
end
end
-- We check for ringing to skip false positives.
if ringing then
if result == sip.Error.BUSY then
return stdnse.format_output(true, "Target line is busy.")
elseif result == sip.Error.DECLINE then
return stdnse.format_output(true, ("Target declined the call. (After %.1f seconds)"):format(waittime / 1000))
elseif result == sip.Error.OK then
return stdnse.format_output(true, ("Target hung up. (After %.1f seconds)"):format(waittime / 1000))
elseif result == sip.Error.TIMEOUT then
return stdnse.format_output(true, "Ringing, no answer.")
elseif result == sip.Error.RING then
return stdnse.format_output(true, "Ringing, got no answer. (script timeout)")
end
else
stdnse.debug1("Target phone didn't ring.")
end
end
|