127 lines
5.2 KiB
Ruby
127 lines
5.2 KiB
Ruby
require 'singleton'
|
|
module PacketFu
|
|
|
|
# Utils is a collection of various and sundry network utilities that are useful for packet
|
|
# manipulation.
|
|
class Utils
|
|
include Singleton
|
|
|
|
# Returns the MAC address of an IP address, or nil if it's not responsive to arp. Takes
|
|
# a dotted-octect notation of the target IP address, as well as a number of parameters:
|
|
#
|
|
# === Parameters
|
|
# :eth_saddr
|
|
# Source MAC address. Defaults to "00:00:00:00:00:00".
|
|
# :ip_saddr
|
|
# Source IP address. Defaults to "0.0.0.0"
|
|
# :flavor
|
|
# The flavor of the ARP request. Defaults to :none.
|
|
# :timeout
|
|
# Timeout in seconds. Defaults to 3.
|
|
#
|
|
# === Example
|
|
# PacketFu::Utils::arp("192.168.1.1") #=> "00:18:39:01:33:70"
|
|
# PacketFu::Utils::arp("192.168.1.1", :timeout => 5, :flavor => :hp_deskjet)
|
|
#
|
|
# === Warning
|
|
#
|
|
# It goes without saying, spewing forged ARP packets on your network is a great way to really
|
|
# irritate your co-workers.
|
|
def self.arp(target_ip,args={})
|
|
arp_pkt = PacketFu::ARPPacket.new(:flavor => (args[:flavor] || :none))
|
|
arp_pkt.eth_saddr = arp_pkt.arp_saddr_mac = (args[:eth_saddr] || ($packetfu_default.config[:eth_saddr] if $packetfu_default) || "00:00:00:00:00:00" )
|
|
arp_pkt.eth_daddr = "ff:ff:ff:ff:ff:ff"
|
|
arp_pkt.arp_daddr_mac = "00:00:00:00:00:00"
|
|
arp_pkt.arp_saddr_ip = (args[:ip_saddr] || ($packetfu_default.config[:ip_saddr] if $packetfu_default) || "0.0.0.0")
|
|
arp_pkt.arp_daddr_ip = target_ip
|
|
iface = (args[:iface] || ($packetfu_default.iface if $packetfu_default) || "eth0")
|
|
# Stick the Capture object in its own thread.
|
|
cap_thread = Thread.new do
|
|
target_mac = nil
|
|
cap = PacketFu::Capture.new(:iface => iface, :start => true,
|
|
:filter => "arp src #{target_ip} and ether dst #{arp_pkt.eth_saddr}")
|
|
arp_pkt.to_w(iface) # Shorthand for sending single packets to the default interface.
|
|
timeout = 0
|
|
while target_mac.nil? && timeout <= (args[:timeout] || 3)
|
|
if cap.save > 0
|
|
arp_response = PacketFu::Packet.parse(cap.array[0])
|
|
target_mac = arp_response.arp_saddr_mac if arp_response.arp_saddr_ip = target_ip
|
|
end
|
|
timeout += 0.1
|
|
sleep 0.1 # Check for a response ten times per second.
|
|
end
|
|
target_mac
|
|
end # cap_thread
|
|
cap_thread.value
|
|
end # def self.arp
|
|
|
|
# Discovers the local IP and Ethernet address, which is useful for writing
|
|
# packets you expect to get a response to. Note, this is a noisy
|
|
# operation; a UDP packet is generated and dropped on to the default (or named)
|
|
# interface, and then captured (which means you need to be root to do this).
|
|
#
|
|
# whoami? returns a hash of :eth_saddr, :eth_src, :ip_saddr, :ip_src,
|
|
# :eth_dst, and :eth_daddr (the last two are usually suitable for a
|
|
# gateway mac address). It's most useful as an argument to PacketFu::Config.new.
|
|
#
|
|
# === Parameters
|
|
# :iface => "eth0"
|
|
# An interface to listen for packets on. Note that since we rely on the OS to send the probe packet,
|
|
# you will need to specify a target which will use this interface.
|
|
# :target => "1.2.3.4"
|
|
# A target IP address. By default, a packet will be sent to a random address in the 177/8 network.
|
|
# Since this network is IANA reserved (for now), this network should be handled by your default gateway
|
|
# and default interface.
|
|
def self.whoami?(args={})
|
|
if args[:iface] =~ /^lo/ # Linux loopback more or less. Need a switch for windows loopback, too.
|
|
dst_host = "127.0.0.1"
|
|
else
|
|
dst_host = (args[:target] || IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET).to_s)
|
|
end
|
|
|
|
dst_port = rand(0xffff-1024)+1024
|
|
msg = "PacketFu whoami? packet #{(Time.now.to_i + rand(0xffffff)+1)}"
|
|
cap = Capture.new(:iface => (args[:iface] || Pcap.lookupdev), :start => true, :filter => "udp and dst host #{dst_host} and dst port #{dst_port}")
|
|
UDPSocket.open.send(msg,0,dst_host,dst_port)
|
|
cap.save
|
|
pkt = Packet.parse(cap.array[0]) unless cap.save.zero?
|
|
timeout = 0
|
|
while timeout < 1 # Sometimes packet generation can be a little pokey.
|
|
if pkt
|
|
timeout = 1.1 # Cancel the timeout
|
|
if pkt.payload == msg
|
|
my_data = {
|
|
:iface => args[:iface] || Pcap.lookupdev || 'lo',
|
|
:pcapfile => args[:pcapfile] || "/tmp/out.pcap",
|
|
:eth_saddr => pkt.eth_saddr,
|
|
:eth_src => pkt.eth_src.to_s,
|
|
:ip_saddr => pkt.ip_saddr,
|
|
:ip_src => pkt.ip_src.to_s,
|
|
:eth_dst => pkt.eth_dst.to_s,
|
|
:eth_daddr => pkt.eth_daddr
|
|
}
|
|
else raise SecurityError,
|
|
"whoami() packet doesn't match sent data. Something fishy's going on."
|
|
end
|
|
else
|
|
sleep 0.1; timeout += 0.1
|
|
cap.save
|
|
pkt = Packet.parse(cap.array[0]) unless cap.save.zero?
|
|
end
|
|
raise SocketError, "Didn't recieve the whomi() packet." if !pkt
|
|
cap = nil
|
|
end
|
|
my_data
|
|
end
|
|
|
|
# This is a brute-force approach at trying to find a suitable interface with an IP address.
|
|
def self.lookupdev
|
|
# XXX cycle through eth0-9 and wlan0-9, and if a cap start throws a RuntimeErorr (and we're
|
|
# root), it's not a good interface. Boy, really ought to fix lookupdev directly with another
|
|
# method that returns an array rather than just the first candidate.
|
|
end
|
|
|
|
|
|
end # class Utils
|
|
|
|
end # module PacketFu |