-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
ssh-publickey-acceptance.nse
167 lines (156 loc) · 5.81 KB
/
ssh-publickey-acceptance.nse
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
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local base64 = require "base64"
local string = require "string"
local table = require "table"
local io = require "io"
local libssh2_util = require "libssh2-utility"
description = [[
This script takes a table of paths to private keys, passphrases, and usernames
and checks each pair to see if the target ssh server accepts them for publickey
authentication. If no keys are given or the known-bad option is given, the
script will check if a list of known static public keys are accepted for
authentication.
]]
---
-- @usage
-- nmap -p 22 --script ssh-publickey-acceptance --script-args "ssh.usernames={'root', 'user'}, ssh.privatekeys={'./id_rsa1', './id_rsa2'}" <target>
--
-- @usage
-- nmap -p 22 --script ssh-publickey-acceptance --script-args 'ssh.usernames={"root", "user"}, publickeys={"./id_rsa1.pub", "./id_rsa2.pub"}' <target>
--
-- @output
-- 22/tcp open ssh syn-ack
-- | ssh-publickey-acceptance:
-- | Accepted Public Keys:
-- |_ Key ./id_rsa1 accepted for user root
--
-- @args ssh.privatekeys Table containing filenames of privatekeys to test
-- @args ssh.passphrases Table containing passphrases for each private key
-- @args ssh.publickeys Table containing filenames of publickkeys to test
-- @args ssh.usernames Table containing usernames to check
-- @args knownbad If specified, check if keys from publickeydb are accepted
-- @args publickeydb Specifies alternative publickeydb
author = "Devin Bjelland"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"auth", "intrusive"}
local privatekeys = stdnse.get_script_args "ssh.privatekeys"
local passphrases = stdnse.get_script_args "ssh.passphrases" or {}
local usernames = stdnse.get_script_args "ssh.usernames"
local knownbad = stdnse.get_script_args "knownbad"
local publickeys = stdnse.get_script_args "ssh.publickeys"
local publickeydb = stdnse.get_script_args "publickeydb" or nmap.fetchfile("nselib/data/publickeydb")
portrule = shortport.ssh
function action (host, port)
local result = stdnse.output_table()
local r = {}
local helper = libssh2_util.SSHConnection:new()
local failures = 0
local successes = 0
if publickeys and usernames then
for j = 1, #usernames do
for i = 1, #publickeys do
stdnse.debug("Checking key: " .. publickeys[i] .. " for user " .. usernames[j])
local status, result = helper:read_publickey(publickeys[i])
if not status then
stdnse.verbose("Error reading key: " .. result)
elseif helper:connect(host, port) then
successes = successes + 1
local status, err = helper:publickey_canauth(usernames[j], result)
if status then
table.insert(r, "Key " .. publickeys[i] .. " accepted for user " .. usernames[j])
stdnse.verbose("Found accepted key: " .. publickeys[i] .. " for user " .. usernames[j])
elseif err then
stdnse.debug("Error in publickey_canauth: %s", err)
end
helper:disconnect()
else
-- Allow 3 connection attempts, then bail
failures = failures + 1
stdnse.debug1("Connect failed.")
if failures > 2 then
if successes == 0 then
-- If we haven't succeeded even once, don't report results.
stdnse.debug1("Giving up.")
return nil
else
goto ACTION_END
end
end
end
end
end
end
if knownbad or not (privatekeys or publickeys) then
for line in io.lines(publickeydb) do
local sections = {}
for section in string.gmatch(line, '([^,]+)') do
table.insert(sections, section)
end
local key = sections[1]
local user = sections[2]
local msg = sections[3]
stdnse.debug("Checking key: " .. key .. " for user " .. user)
key = base64.dec(key)
if helper:connect(host, port) then
successes = successes + 1
if helper:publickey_canauth(user, key) then
table.insert(r, msg)
stdnse.verbose("Found accepted key: " .. msg)
end
helper:disconnect()
else
-- Allow 3 connection attempts, then bail
failures = failures + 1
stdnse.debug1("Connect failed.")
if failures > 2 then
if successes == 0 then
-- If we haven't succeeded even once, don't report results.
stdnse.debug1("Giving up.")
return nil
else
goto ACTION_END
end
end
end
end
end
if privatekeys and usernames then
for j = 1, #usernames do
for i = 1, #privatekeys do
stdnse.debug("Checking key: " .. privatekeys[i] .. " for user " .. usernames[j])
if helper:connect(host, port) then
successes = successes + 1
if not helper:publickey_auth(usernames[j], privatekeys[i], passphrases[i] or "") then
stdnse.verbose "Failed to authenticate"
else
table.insert(r, "Key " .. privatekeys[i] .. " accepted for user " .. usernames[j])
stdnse.verbose("Found accepted key: " .. privatekeys[i] .. " for user " .. usernames[j])
end
helper:disconnect()
else
-- Allow 3 connection attempts, then bail
failures = failures + 1
stdnse.debug1("Connect failed.")
if failures > 2 then
if successes == 0 then
-- If we haven't succeeded even once, don't report results.
stdnse.debug1("Giving up.")
return nil
else
goto ACTION_END
end
end
end
end
end
end
::ACTION_END::
if #r > 0 then
result["Accepted Public Keys"] = r
else
result["Accepted Public Keys"] = "No public keys accepted"
end
return result
end