// Copyright 2012 Google, Inc. All rights reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file in the root of the source // tree. // synscan implements a TCP syn scanner on top of pcap. // It's more complicated than arpscan, since it has to handle sending packets // outside the local network, requiring some routing and ARP work. // // Since this is just an example program, it aims for simplicity over // performance. It doesn't handle sending packets very quickly, it scans IPs // serially instead of in parallel, and uses gopacket.Packet instead of // gopacket.DecodingLayerParser for packet processing. We also make use of very // simple timeout logic with time.Since. // // Making it blazingly fast is left as an exercise to the reader. package main import ( "errors" "flag" "log" "net" "time" "github.com/google/gopacket" "github.com/google/gopacket/examples/util" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" "github.com/google/gopacket/routing" ) // scanner handles scanning a single IP address. type scanner struct { // iface is the interface to send packets on. iface *net.Interface // destination, gateway (if applicable), and source IP addresses to use. dst, gw, src net.IP handle *pcap.Handle // opts and buf allow us to easily serialize packets in the send() // method. opts gopacket.SerializeOptions buf gopacket.SerializeBuffer } // newScanner creates a new scanner for a given destination IP address, using // router to determine how to route packets to that IP. func newScanner(ip net.IP, router routing.Router) (*scanner, error) { s := &scanner{ dst: ip, opts: gopacket.SerializeOptions{ FixLengths: true, ComputeChecksums: true, }, buf: gopacket.NewSerializeBuffer(), } // Figure out the route to the IP. iface, gw, src, err := router.Route(ip) if err != nil { return nil, err } log.Printf("scanning ip %v with interface %v, gateway %v, src %v", ip, iface.Name, gw, src) s.gw, s.src, s.iface = gw, src, iface // Open the handle for reading/writing. // Note we could very easily add some BPF filtering here to greatly // decrease the number of packets we have to look at when getting back // scan results. handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever) if err != nil { return nil, err } s.handle = handle return s, nil } // close cleans up the handle. func (s *scanner) close() { s.handle.Close() } // getHwAddr is a hacky but effective way to get the destination hardware // address for our packets. It does an ARP request for our gateway (if there is // one) or destination IP (if no gateway is necessary), then waits for an ARP // reply. This is pretty slow right now, since it blocks on the ARP // request/reply. func (s *scanner) getHwAddr() (net.HardwareAddr, error) { start := time.Now() arpDst := s.dst if s.gw != nil { arpDst = s.gw } // Prepare the layers to send for an ARP request. eth := layers.Ethernet{ SrcMAC: s.iface.HardwareAddr, DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, EthernetType: layers.EthernetTypeARP, } arp := layers.ARP{ AddrType: layers.LinkTypeEthernet, Protocol: layers.EthernetTypeIPv4, HwAddressSize: 6, ProtAddressSize: 4, Operation: layers.ARPRequest, SourceHwAddress: []byte(s.iface.HardwareAddr), SourceProtAddress: []byte(s.src), DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, DstProtAddress: []byte(arpDst), } // Send a single ARP request packet (we never retry a send, since this // is just an example ;) if err := s.send(ð, &arp); err != nil { return nil, err } // Wait 3 seconds for an ARP reply. for { if time.Since(start) > time.Second*3 { return nil, errors.New("timeout getting ARP reply") } data, _, err := s.handle.ReadPacketData() if err == pcap.NextErrorTimeoutExpired { continue } else if err != nil { return nil, err } packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil { arp := arpLayer.(*layers.ARP) if net.IP(arp.SourceProtAddress).Equal(net.IP(arpDst)) { return net.HardwareAddr(arp.SourceHwAddress), nil } } } } // scan scans the dst IP address of this scanner. func (s *scanner) scan() error { // First off, get the MAC address we should be sending packets to. hwaddr, err := s.getHwAddr() if err != nil { return err } // Construct all the network layers we need. eth := layers.Ethernet{ SrcMAC: s.iface.HardwareAddr, DstMAC: hwaddr, EthernetType: layers.EthernetTypeIPv4, } ip4 := layers.IPv4{ SrcIP: s.src, DstIP: s.dst, Version: 4, TTL: 64, Protocol: layers.IPProtocolTCP, } tcp := layers.TCP{ SrcPort: 54321, DstPort: 0, // will be incremented during the scan SYN: true, } tcp.SetNetworkLayerForChecksum(&ip4) // Create the flow we expect returning packets to have, so we can check // against it and discard useless packets. ipFlow := gopacket.NewFlow(layers.EndpointIPv4, s.dst, s.src) start := time.Now() for { // Send one packet per loop iteration until we've sent packets // to all of ports [1, 65535]. if tcp.DstPort < 65535 { start = time.Now() tcp.DstPort++ if err := s.send(ð, &ip4, &tcp); err != nil { log.Printf("error sending to port %v: %v", tcp.DstPort, err) } } // Time out 5 seconds after the last packet we sent. if time.Since(start) > time.Second*5 { log.Printf("timed out for %v, assuming we've seen all we can", s.dst) return nil } // Read in the next packet. data, _, err := s.handle.ReadPacketData() if err == pcap.NextErrorTimeoutExpired { continue } else if err != nil { log.Printf("error reading packet: %v", err) continue } // Parse the packet. We'd use DecodingLayerParser here if we // wanted to be really fast. packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) // Find the packets we care about, and print out logging // information about them. All others are ignored. if net := packet.NetworkLayer(); net == nil { // log.Printf("packet has no network layer") } else if net.NetworkFlow() != ipFlow { // log.Printf("packet does not match our ip src/dst") } else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer == nil { // log.Printf("packet has not tcp layer") } else if tcp, ok := tcpLayer.(*layers.TCP); !ok { // We panic here because this is guaranteed to never // happen. panic("tcp layer is not tcp layer :-/") } else if tcp.DstPort != 54321 { // log.Printf("dst port %v does not match", tcp.DstPort) } else if tcp.RST { log.Printf(" port %v closed", tcp.SrcPort) } else if tcp.SYN && tcp.ACK { log.Printf(" port %v open", tcp.SrcPort) } else { // log.Printf("ignoring useless packet") } } } // send sends the given layers as a single packet on the network. func (s *scanner) send(l ...gopacket.SerializableLayer) error { if err := gopacket.SerializeLayers(s.buf, s.opts, l...); err != nil { return err } return s.handle.WritePacketData(s.buf.Bytes()) } func main() { defer util.Run()() router, err := routing.New() if err != nil { log.Fatal("routing error:", err) } for _, arg := range flag.Args() { var ip net.IP if ip = net.ParseIP(arg); ip == nil { log.Printf("non-ip target: %q", arg) continue } else if ip = ip.To4(); ip == nil { log.Printf("non-ipv4 target: %q", arg) continue } // Note: newScanner creates and closes a pcap Handle once for // every scan target. We could do much better, were this not an // example ;) s, err := newScanner(ip, router) if err != nil { log.Printf("unable to create scanner for %v: %v", ip, err) continue } if err := s.scan(); err != nil { log.Printf("unable to scan %v: %v", ip, err) } s.close() } }