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

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Capture

  def initialize
    super(
      'Name'        => 'Rogue Gateway Detection: Receiver',
      'Description' => %q{
        This module listens for replies to the requests sent by
      the rogue_send module. The RPORT, CPORT, and ECHOID values
      must match the rogue_send parameters used exactly.
      },
      'Author'      => 'hdm',
      'License'     => MSF_LICENSE
    )

    register_options([
      OptPort.new("RPORT", [true, "The destination port for the TCP SYN packet", 80]),
      OptPort.new("CPORT", [true, "The source port for the TCP SYN packet", 13832]),
      OptInt.new("ECHOID", [true, "The unique ICMP ECHO ID to embed into the packet", 7893]),
    ])
  end

  def build_filter
    "(icmp and icmp[0] == 0) or (" +
      "tcp and (tcp[13] == 0x12 or (tcp[13] & 0x04) != 0) and " +
      "src port #{datastore['RPORT']} and dst port #{datastore['CPORT']} " +
    ")"
  end

  def run
    open_pcap('SNAPLEN' => 128, 'FILTER' => build_filter)
    print_status("Opening the capture interface...")

    print_status("Waiting for responses to rogue_send...")
    begin
    each_packet do |pkt|
      r = parse_reply(pkt)
      next if not r
      print_status("Reply from #{r[:internal]} using gateway #{r[:external]} (#{r[:type].to_s.upcase})")
    end
    rescue ::Interrupt
      raise $!
    ensure
      close_pcap
    end
  end

  def parse_reply(r)
    p = PacketFu::Packet.parse(r)
    return unless p.is_eth?
    if p.is_icmp?
      return if(p.payload[0,2] != [datastore['ECHOID']].pack("n"))
      return unless p.payload.size >= 8
      reply = {:raw => p}
      reply[:type] = :icmp
      reply[:internal] = Rex::Socket.addr_nota(p.payload[4,4])
      reply[:external] = p.ip_saddr
    elsif p.is_tcp?
      return if p.tcp_ack.zero?
      reply = {:packet => p}
      reply[:type] = :tcp
      reply[:internal] = Rex::Socket.addr_itoa(p.tcp_ack - 1)
      reply[:external] = p.ip_saddr
    else
      reply = nil
    end
    return reply
  end
end
