-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathssh-os.nse
More file actions
231 lines (186 loc) · 6.88 KB
/
ssh-os.nse
File metadata and controls
231 lines (186 loc) · 6.88 KB
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
local shortport = require "shortport"
local stdnse = require "stdnse"
local comm = require "comm"
local ubuntu_versions = require "tables.ubuntu-versions"
local freebsd_versions = require "tables.freebsd-versions"
local debian_versions = require "tables.debian-versions"
description = [[
Identifies Ubuntu, FreeBSD, Debian, or Raspbian version based on response of SSH banner.
Identifies the following versions:
Ubuntu 4.10 to 25.10
FreeBSD 4.3 to 15.0-RELEASE
Debian 3.x to 13.x
Raspbian 7.x to 11.x (tentative 11.x version recognition)
Note: The accuracy of the response is based on the default banner response.
A number of scenarios may provide an inaccurate result from the target host:
* different OpenSSH version or alternative SSH server installed
* edited/omitted banner via sshd_config
* hexedit of OpenSSH binary; modified banner
* recompiled OpenSSH
]]
-- @usage
-- nmap -p22 -sV --script ssh-os.nse <target>
-- OR
-- nmap -p <port number> -sV --script ssh-os.nse <target>
--
-- @output
-- PORT STATE SERVICE REASON VERSION
--22/tcp open ssh syn-ack OpenSSH 6.0p1 Debian 3ubuntu1.2 (Ubuntu Linux; protocol 2.0)
--| ssh-os:
--| Linux Version: Ubuntu 12.10 Quantal Quetzal
--| SSH Version + Build Number: 6.0p1-3
--|_ SSH Banner: SSH-2.0-OpenSSH_6.0p1 Debian-3ubuntu1.2\x0D
--Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
--
--
--
-- List of default banners for reference:
-- https://github.com/richlamdev/ssh-default-banners
-- https://github.com/rapid7/recog/blob/master/xml/ssh_banners.xml
--
-- SSH Banner format: RFC 4253
-- https://tools.ietf.org/html/rfc4253
--
--
-- Typical Ubuntu SSH version banner:
-- SSH-2.0-OpenSSH_5.9p1 Debian-5ubuntu1.1
--
-- Breakdown:
--
-- SSH Proto Ver OpenSSH Ver Portable Ver Build Ver Patch Ver
-- SSH-2.0 OpenSSH_5.9 p1 Debian-5 ubuntu1.1
author = "Richard Lam <richlam.dev at gmail.com>"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default", "safe", "discovery", "version"}
---
-- obtain SSH+portable+build versions to identify Ubuntu version.
-- @param ssh_banner to be evaluated against regex
-- @return Ubuntu version and build number
local function get_ubuntu(ssh_banner)
local ubuntu_ver =""
local u_ssh_build = ""
local u_build_version = ""
local u_ssh_version = ""
-- Find position of "OpenSSH_" dynamically for flexibility
local ver_start = ssh_banner:find("OpenSSH_")
if not ver_start then
return "Not OpenSSH", ""
end
ver_start = ver_start + 8 -- skip "OpenSSH_"
-- Try longer version first
u_ssh_version = ssh_banner:match("%d+%.%d+%.%dp%d+", ver_start)
if not u_ssh_version then
u_ssh_version = ssh_banner:match("%d+%.%dp%d+", ver_start)
end
if not u_ssh_version then
return "Unknown Ubuntu version", ""
end
-- After the SSH version, skip any spaces or "_Ubuntu-" etc.
local build_start = ssh_banner:find("Ubuntu%-", ver_start)
if build_start then
-- Build numbers like "-5ubuntu5"
u_build_version = ssh_banner:match("%-%d+", build_start)
end
u_ssh_build = u_ssh_version .. u_build_version
-- lookup version from tables/ubuntu-versions.lua
if ubuntu_versions[u_ssh_build] then
ubuntu_ver = ubuntu_versions[u_ssh_build]
else
ubuntu_ver = "Unknown Ubuntu version"
end
return ubuntu_ver,u_ssh_build
end
---
-- obtain last eight digits(date) of banner to identify FreeBSD version.
-- @param ssh_banner to be evaluated against regex
-- @return FreeBSD version
local function get_freebsd(ssh_banner)
local freebsd_ver = ""
local f_ssh_version = ""
-- determine longer banner with hpn13v11
if ssh_banner:match("hpn13v11",17) then
f_ssh_version = ssh_banner:match("%d+",37)
else
f_ssh_version = ssh_banner:match("%d+",28)
end
-- lookup version from tables/freebsd-versions.lua
if freebsd_versions[f_ssh_version] then
freebsd_ver = freebsd_versions[f_ssh_version]
else
freebsd_ver = "Unknown FreeBSD version"
end
return freebsd_ver
end
---
-- obtain SSH+portable+build versions to identify Debian or Raspbian version.
-- @param ssh_banner to be evaluated against regex
-- @return Debian version and build number
local function get_debian(ssh_banner)
local debian_ver =""
local d_ssh_build = ""
local d_build_version = ""
local d_ssh_version = ""
local start_offset = ""
-- start the match at 17 chars; typically: SSH-2.0-OpenSSH_
-- identify longer SSH version length, eg. 6.6.1p1
if ssh_banner:match("%d+%.%d+%.%dp%d+",17) then
d_ssh_version = ssh_banner:match("%d+%.%d+%.%dp%d+",17)
else
-- identify shorter SSH version length eg. 6.6p2
d_ssh_version = ssh_banner:match("%d+%.%d+p%d+",17)
end
-- add 8 for Debian, or 10 for Raspbian to obtain build number
if ssh_banner:match ("Debian",22) then
start_offset = 16 + string.len(d_ssh_version) + 8
elseif ssh_banner:match ("Raspbian",22) then
start_offset = 16 + string.len(d_ssh_version) + 10
end
-- obtain build version and concat to SSH version, then lookup version
d_build_version = ssh_banner:match("%-%d+",start_offset)
d_ssh_build = d_ssh_version .. d_build_version
-- lookup version from tables/debian-versions.lua
if debian_versions[d_ssh_build] then
debian_ver = debian_versions[d_ssh_build]
else
debian_ver = "Unknown Debian based (or Raspbian) version"
end
return debian_ver,d_ssh_build
end
portrule = shortport.port_or_service( 22 , "ssh", "tcp", "open")
action = function (host, port)
local distro_type =""
local ssh_build = ""
local misc_os_type = ""
local response = stdnse.output_table()
local ssh_status, ssh_banner = comm.get_banner(host, port, {lines=1})
if not ssh_status then
return
end
-- OpenSSH based identification
if ssh_banner:match("OpenSSH_",7) then
if ssh_banner:match("[uU]buntu",17) then
distro_type,ssh_build = get_ubuntu(ssh_banner)
response["Linux Version"] = distro_type
response["SSH Version + Build Number"] = ssh_build
-- Ubuntu 13.04 is the only version that does not have the string
-- "[uU]buntu" embedded in the SSH version banner
-- (Also, Debian does not have a version released with OpenSSH 6.1p1)
elseif ssh_banner:match("6%.1p1%sDebian%-",17) then
distro_type,ssh_build = get_ubuntu(ssh_banner)
response["Linux Version"] = distro_type
response["SSH Version + Build Number"] = ssh_build
elseif ssh_banner:match("FreeBSD",20) then
distro_type = get_freebsd(ssh_banner)
response["BSD Version"] = distro_type
elseif (ssh_banner:match("Debian", 22)) or (ssh_banner:match("Raspbian", 22)) then
distro_type,ssh_build = get_debian(ssh_banner)
response["Linux Version"] = distro_type
response["SSH Version + Build Number"] = ssh_build
end
else --potential to identify non-OpenSSH banners in the future
distro_type = "Unrecognized SSH banner."
response["Linux/Unix Version"] = distro_type
end
response["SSH Banner"] = ssh_banner
return response
end