Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #18028, Add Apache NiFi login scanner module
- Loading branch information
Showing
2 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
111 changes: 111 additions & 0 deletions
111
documentation/modules/auxiliary/scanner/http/apache_nifi_login.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
## Vulnerable Application | ||
|
||
This module attempts to take login details for Apache NiFi websites | ||
and identify if they are valid or not. | ||
|
||
Tested against NiFi major releases 1.14.0 - 1.21.0, and 1.13.0 | ||
Also works against NiFi <= 1.13.0, but the module needs to be adjusted: | ||
|
||
- set SSL false | ||
- set rport 8080 | ||
|
||
### Docker Install | ||
|
||
Apache manages Docker installs for NiFi with version numbers, simply select the version number you wish to install. Examples: | ||
|
||
``` | ||
docker run -p 8443:8443 -d apache/nifi:1.21.0 | ||
docker run -p 8443:8443 -d apache/nifi:1.20.0 | ||
docker run -p 8443:8443 -d apache/nifi:1.19.0 | ||
docker run -p 8443:8443 -d apache/nifi:1.18.0 | ||
docker run -p 8443:8443 -d apache/nifi:1.17.0 | ||
docker run -p 8443:8443 -d apache/nifi:1.16.0 | ||
docker run -p 8443:8443 -d apache/nifi:1.15.0 | ||
docker run -p 8443:8443 -d apache/nifi:1.14.0 | ||
docker run -p 8080:8080 -d apache/nifi:1.13.0 | ||
``` | ||
|
||
Versions > 1.13.0 dynamically create a username and password. To view them in the docker logs, use the following command: | ||
``` | ||
docker logs <container> | grep Generated | ||
``` | ||
|
||
|
||
## Verification Steps | ||
|
||
1. Install the application | ||
1. Start msfconsole | ||
1. Do: `use auxiliary/scanner/http/apache_nifi_login` | ||
1. Do: `set rhosts [ip]` | ||
1. Do: `set username [username]` | ||
1. Do: `set password [password]` | ||
1. Do: `run` | ||
1. If any logins are valid, they will be printed | ||
|
||
## Options | ||
|
||
## Scenarios | ||
|
||
### Docker image of Apache NiFi 1.18.0 | ||
|
||
``` | ||
msf6 > use auxiliary/scanner/http/nifi_login | ||
msf6 auxiliary(scanner/http/nifi_login) > set rhosts 127.0.0.1 | ||
rhosts => 127.0.0.1 | ||
msf6 auxiliary(scanner/http/nifi_login) > set username 21acf672-7935-441c-a38b-b52643f029bf | ||
username => 21acf672-7935-441c-a38b-b52643f029bf | ||
msf6 auxiliary(scanner/http/nifi_login) > set password bad | ||
password => bad | ||
msf6 auxiliary(scanner/http/nifi_login) > run | ||
[*] Checking 127.0.0.1 | ||
[-] 127.0.0.1:8443 - Apache NiFi - Failed to login as '21acf672-7935-441c-a38b-b52643f029bf' with password 'bad' | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
msf6 auxiliary(scanner/http/nifi_login) > set password R4+xdl8P9Phrqne4NxHDponQs5X9ktn2 | ||
password => R4+xdl8P9Phrqne4NxHDponQs5X9ktn2 | ||
msf6 auxiliary(scanner/http/nifi_login) > run | ||
[*] Checking 127.0.0.1 | ||
[+] 127.0.0.1:8443 - Apache NiFi - Login successful as '21acf672-7935-441c-a38b-b52643f029bf' with password 'R4+xdl8P9Phrqne4NxHDponQs5X9ktn2' | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
``` | ||
|
||
### Docker image of Apache NiFi 1.21.0 | ||
``` | ||
msf6 > use auxiliary/scanner/http/apache_nifi_login | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > set RHOST 127.0.0.1 | ||
RHOST => 127.0.0.1 | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > set RPORT 8443 | ||
RPORT => 8443 | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > set USERNAME test | ||
USERNAME => test | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > set PASSWORD test | ||
PASSWORD => test | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > run | ||
[*] Checking 127.0.0.1 | ||
[-] 127.0.0.1:8443 - Apache NiFi - Failed to login as 'test' with password 'test' | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > set USERNAME a43c5a33-1635-46aa-8773-ef65f572fa0e | ||
USERNAME => a43c5a33-1635-46aa-8773-ef65f572fa0e | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > set PASSWORD QUicCmARFZKeaO1QqPTdnJlB/IPCjJ3u | ||
PASSWORD => QUicCmARFZKeaO1QqPTdnJlB/IPCjJ3u | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > run | ||
[*] Checking 127.0.0.1 | ||
[+] 127.0.0.1:8443 - Apache NiFi - Login successful as 'a43c5a33-1635-46aa-8773-ef65f572fa0e' with password 'QUicCmARFZKeaO1QqPTdnJlB/IPCjJ3u' | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > creds | ||
Credentials | ||
=========== | ||
host origin service public private realm private_type JtR Format | ||
---- ------ ------- ------ ------- ----- ------------ ---------- | ||
127.0.0.1 127.0.0.1 8443/tcp (https) a43c5a33-1635-46aa-8773-ef65f572fa0e QUicCmARFZKeaO1QqPTdnJlB/IPCjJ3u Password | ||
msf6 auxiliary(scanner/http/apache_nifi_login) > | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
## | ||
# This module requires Metasploit: https://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
|
||
class MetasploitModule < Msf::Auxiliary | ||
include Msf::Exploit::Remote::HttpClient | ||
include Msf::Auxiliary::Scanner | ||
include Msf::Auxiliary::AuthBrute | ||
include Msf::Auxiliary::Report | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'Apache NiFi Login Scanner', | ||
'Description' => %q{ | ||
This module attempts to take login details for Apache NiFi websites | ||
and identify if they are valid or not. | ||
Tested against NiFi major releases 1.14.0 - 1.21.0, and 1.13.0 | ||
Also works against NiFi <= 1.13.0, but the module needs to be adjusted: | ||
set SSL false | ||
set rport 8080 | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => [ | ||
'h00die', # msf module | ||
], | ||
'Notes' => { | ||
'Stability' => [CRASH_SAFE], | ||
'Reliability' => [], | ||
'SideEffects' => [IOC_IN_LOGS] | ||
} | ||
) | ||
) | ||
register_options( | ||
[ | ||
Opt::RPORT(8443), | ||
OptString.new('TARGETURI', [ true, 'The URI of the Apache NiFi Application', '/']) | ||
] | ||
) | ||
register_advanced_options([ | ||
OptBool.new('SSL', [true, 'Negotiate SSL connection', true]) | ||
]) | ||
end | ||
|
||
def report_cred(opts) | ||
service_data = { | ||
address: opts[:ip], | ||
port: opts[:port], | ||
service_name: opts[:service_name], | ||
protocol: 'tcp', | ||
workspace_id: myworkspace_id | ||
} | ||
|
||
credential_data = { | ||
origin_type: :service, | ||
module_fullname: fullname, | ||
username: opts[:user], | ||
private_data: opts[:password], | ||
private_type: :password | ||
}.merge(service_data) | ||
|
||
login_data = { | ||
core: create_credential(credential_data), | ||
status: Metasploit::Model::Login::Status::SUCCESSFUL, | ||
last_attempted_at: DateTime.now, | ||
proof: opts[:proof] | ||
}.merge(service_data) | ||
|
||
create_credential_login(login_data) | ||
end | ||
|
||
def run_host(ip) | ||
vprint_status("Checking #{ip}") | ||
res = send_request_cgi!( | ||
'uri' => normalize_uri(target_uri.path, 'nifi', 'login') | ||
) | ||
|
||
fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? | ||
fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200 | ||
|
||
fail_with(Failure::NotVulnerable, "Apache NiFi not detected on #{ip}") unless res.body =~ %r{js/nf/nf-namespace\.js\?([\d.]*)">} | ||
|
||
res = send_request_cgi!( | ||
'uri' => normalize_uri(target_uri.path, 'nifi-api', 'access', 'config') | ||
) | ||
fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? | ||
fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200 | ||
|
||
res_json = res.get_json_document | ||
|
||
unless res_json.dig('config', 'supportsLogin') | ||
print_error("#{peer} - User login not supported, try visiting /nifi to gain access") | ||
return | ||
end | ||
|
||
each_user_pass do |user, pass| | ||
res = send_request_cgi!( | ||
'uri' => normalize_uri(target_uri.path, 'nifi-api', 'access', 'token'), | ||
'method' => 'POST', | ||
'vars_post' => { | ||
'username' => user, | ||
'password' => pass | ||
} | ||
) | ||
fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? | ||
if res.code == 201 | ||
print_good("#{peer} - Apache NiFi - Login successful as '#{user}' with password '#{pass}'") | ||
report_cred( | ||
ip: rhost, | ||
port: rport, | ||
service_name: (ssl ? 'https' : 'http'), | ||
user: user, | ||
password: pass, | ||
proof: res.body.to_s | ||
) | ||
elsif res.code == 409 | ||
fail_with(Failure::BadConfig, "#{peer} - Logins only accepted on HTTPS") | ||
else | ||
vprint_error("#{peer} - Apache NiFi - Failed to login as '#{user}' with password '#{pass}'") | ||
end | ||
end | ||
end | ||
end |