summaryrefslogtreecommitdiffstats
path: root/scripts/unusual-port.nse
blob: 2dd83bb2511eb617f839fd0805283c648251286a (plain)
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
local datafiles = require "datafiles"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"

description = [[
Compares the detected service on a port against the expected service for that
port number (e.g. ssh on 22, http on 80) and reports deviations. The script
requires that a version scan has been run in order to be able to discover what
service is actually running on each port.
]]

---
-- @usage
-- nmap --script unusual-port <ip>
--
-- @output
-- 23/tcp open   ssh     OpenSSH 5.8p1 Debian 7ubuntu1 (protocol 2.0)
-- |_unusual-port: ssh unexpected on port tcp/23
-- 25/tcp open   smtp    Postfix smtpd
--

author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = { "safe" }


local svc_table

portrule = function()
  local status
  status, svc_table = datafiles.parse_services()
  if not status then
    return false --Can't check if we don't have a table!
  end
  return true
end

hostrule = function() return true end

-- the hostrule is only needed to warn
hostaction = function(host)
  local port, state = nil, "open"
  local is_version_scan = false

  -- iterate over ports and check whether name_confidence > 3 this would
  -- suggest that a version scan has been run
  for _, proto in ipairs({"tcp", "udp"}) do
    repeat
      port = nmap.get_ports(host, port, proto, state)
      if ( port and port.version.name_confidence > 3 ) then
        is_version_scan = true
        break
      end
    until( not(port) )
  end

  -- if no version scan has been run, warn the user as the script requires a
  -- version scan in order to work.
  if ( not(is_version_scan) ) then
    return stdnse.format_output(true, "WARNING: this script depends on Nmap's service/version detection (-sV)")
  end

end

portchecks = {

  ['tcp'] = {
    [113] = function(host, port) return ( port.service == "ident" ) end,
    [445] = function(host, port) return ( port.service == "netbios-ssn" ) end,
    [587] = function(host, port) return ( port.service == "smtp" ) end,
    [593] = function(host, port) return ( port.service == "ncacn_http" ) end,
    [636] = function(host, port) return ( port.service == "ldapssl" ) end,
    [3268] = function(host, port) return ( port.service == "ldap" ) end,
  },

  ['udp'] = {
    [5353] = function(host, port) return ( port.service == "mdns" ) end,
  }

}

servicechecks = {
  ['http'] = function(host, port)
    local service = port.service
    port.service = "unknown"
    local status = shortport.http(host, port)
    port.service = service
    return status
  end,

  -- accept msrpc on any port for now, we might want to limit it to certain
  -- port ranges in the future.
  ['msrpc'] = function(host, port) return true end,

  -- accept ncacn_http on any port for now, we might want to limit it to
  -- certain port ranges in the future.
  ['ncacn_http'] = function(host, port) return true end,
}

portaction = function(host, port)
  local ok = false

  if ( port.version.name_confidence <= 3 ) then
    return
  end
  if ( portchecks[port.protocol][port.number] ) then
    ok = portchecks[port.protocol][port.number](host, port)
  end
  if ( not(ok) and servicechecks[port.service] ) then
    ok = servicechecks[port.service](host, port)
  end
  if ( not(ok) and port.service and
      ( port.service == svc_table[port.protocol][port.number] or
      "unknown" == svc_table[port.protocol][port.number] or
      not(svc_table[port.protocol][port.number]) ) ) then
    ok = true
  end
  if ( not(ok) ) then
    return ("%s unexpected on port %s/%d"):format(port.service, port.protocol, port.number)
  end
end

local Actions = {
  hostrule = hostaction,
  portrule = portaction
}

-- execute the action function corresponding to the current rule
action = function(...) return Actions[SCRIPT_TYPE](...) end