安全开发之 gopacket使用技巧

admin 2024年3月3日04:59:39评论11 views字数 6166阅读20分33秒阅读模式


安全开发之 gopacket使用技巧

项目地址 https://github.com/google/gopacket

gopackge 是google开发的一个基于libpacap实现的可以发包和过滤包的golang库

我自己常常用来实现tcp udp icmp包的发送和过滤处理

比如端口扫描就可以使用它来实现快速发包

如ksubdomain也是如此可以通过自己构造udp包来发送实现快速爆破域名

今天我们分享一下 gopacket的简单使用

官方对go有一些要求

所需的最低 Go 版本是 1.5,除了 pcapgo/EthernetHandle、afpacket 和 bsdbpf,由于 x/sys/unix 依赖性,它们至少需要 1.9。

并且需要安装 libpacp 以及 libpacp-dev

如果想发送一个包就需要指定网卡 网卡mac地址 网卡ip地址 以及下一跳的mac地址才能将构建的包发送出去

获取下一跳地址以及相关的mac地址

基于网络基础 我们知道我们电脑里也是有路由表的 以及arp表 arp用于存储 我们寻找ip对应的mac地址 因为实际传输的二层就是mac地址 三层是ip地址 路由表中存有下一跳的ip地址 那我们再根据ip地址通过arp表获取到mac地址 那我们怎么确定哪个是下一跳的ip地址呢

路由表的匹配是基于最长前缀匹配的 基于伟大的开源 我们可以使用 netlink库来 实现 传递个ip 获取匹配的路由条目

项目地址是这个 github.com/vishvananda/netlink

这里只针对 linux 系统

如下代码就是利用 netlink库来获取 出口的网卡及ip地址 以及下一跳的mac地址 (出口网卡的mac地址可以通过 iface.HardwareAddr来获取)

func GetNic(targetIP net.IP) (*NetWorkCardInfo, error) {
    routes, err := netlink.RouteGet(targetIP)
    if err != nil {
        return nil, err
    }

    if len(routes) == 0 {
        return nil, errors.New(fmt.Sprintf("no route to %s", targetIP))
    }
    link, err := netlink.LinkByIndex(routes[0].LinkIndex)
    if err != nil {
        return nil, err
    }
    iface := &net.Interface{
        Index:        link.Attrs().Index,
        MTU:          link.Attrs().MTU,
        Name:         link.Attrs().Name,
        HardwareAddr: link.Attrs().HardwareAddr,
        Flags:        link.Attrs().Flags,
    }
    nextHopIP := routes[0].Gw
    nextHopMAC, err := getMACByIP(nextHopIP)
    if err != nil {
        return nil, err
    }
    // 获取网卡接口的 IP 地址
    addrs, err := iface.Addrs()
    if err != nil {
        return nil, err
    }

    var ipAddr net.IP
    for _, addr := range addrs {
        if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
            if ipNet.IP.To4() != nil {
                ipAddr = ipNet.IP
                break
            }
        }
    }
    return &NetWorkCardInfo{
        Interface:  iface,
        NextHopMac: nextHopMAC,
        IPAddress:  ipAddr,
    }, nil

}

我们现在有了构建的一个包的最基础的条件 那我们来发送一个syn的包

构建及发送包

我们发送syn包需要源端口来与对端端口进行通信 那我们就需要先获取一个空闲端口

获取空闲端口

func getFreePort() (interror) {
    addr, err := net.ResolveTCPAddr("tcp""localhost:0")
    if err != nil {
        return 0, err
    }

    l, err := net.ListenTCP("tcp", addr)
    if err != nil {
        return 0, err
    }
    defer l.Close()
    return l.Addr().(*net.TCPAddr).Port, nil
}

构建一个包

  1. 1. 二层包也就是 ethLayer 需要网卡出口的mac地址 下一跳的mac地址 以及 IPV4类型的包定义

  2. 2. 定义一个ip包 三层包 网络层协议 目的为了指定源目的ip

  3. 3. 定义一个tcp包 这里是传输层 指定了源目的端口 指定我们发的包的flag是 syn

  4. 4. 序列号包 通过我们获取的出口网卡发送包

srcPort, err := getFreePort()
        if err != nil {
            log.Logger.Errorf("获取空闲端口错误: %s", err)
            return
        }
        ethLayer := layers.Ethernet{
            SrcMAC:       r.Conf.Iface.HardwareAddr,
            DstMAC:       r.Conf.NextHopMac,
            EthernetType: layers.EthernetTypeIPv4,
        }

        ipLayer := layers.IPv4{
            SrcIP:    r.Conf.IPAddress,
            DstIP:    net.ParseIP(ip),
            Version:  4,
            TTL:      64,
            Protocol: layers.IPProtocolTCP,
        }

        tcpLayer := layers.TCP{
            SrcPort: layers.TCPPort(srcPort),
            DstPort: layers.TCPPort(port),
            SYN:     true,
        }
        tcpLayer.SetNetworkLayerForChecksum(&ipLayer)
        // 序列化和发送包
        buffer := gopacket.NewSerializeBuffer()
        opts := gopacket.SerializeOptions{
            FixLengths:       true,
            ComputeChecksums: true,
        }
        errSerial := gopacket.SerializeLayers(buffer, opts, &ethLayer, &ipLayer, &tcpLayer,
            gopacket.Payload([]byte("recar")))
        if errSerial != nil {
            log.Logger.Errorf("SYN SerializeLayers error: %s ->", errSerial, ip)
        }
        errWriteData := r.NetWorkCardHandle.WritePacketData(buffer.Bytes())
        if errWriteData != nil {
            log.Logger.Errorf("Failed to send packet: %v", errWriteData)
            return
        }

我们既然发送了包 那我们如何判断对面的端口是通的或者说目标端口回了包呢

我们通过指定网卡 并设置bpf过滤来实现

过滤网卡获取包信息

  1. 1. 通过网卡名来指定获取网卡句柄 (多协程的时候打开一个就可以)

  2. 2. 设置过滤器 就是bpf过滤条件 tcp协议且是ack的

  3. 3. 开始循环监听获取next包 解析包判断是否符合我们的要求后 转换类型 获取 四元组信息输出

var (
            snapshot_len int32         = 1024
            promiscuous  bool          = false
            timeout      time.Duration = -1 * time.Second
            handle       *pcap.Handle
        )
        var err error
        handle, err = pcap.OpenLive(
            r.Conf.Iface.Name,
            snapshot_len,
            promiscuous,
            timeout,
        )
        if err != nil {
            log.Logger.Errorf("监听SYNC pcap打开失败:%sn", err.Error())
            return
        }
        // 设置过滤器
        if err := handle.SetBPFFilter("tcp and tcp[13] == 0x12"); err != nil {
            log.Logger.Errorf("设置BPF 过滤规则异常: %v", err)
            return
        }
        defer handle.Close()
        packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
        for {
            select {
            case <-ctx.Done():
                return
            default:
                packet, err := packetSource.NextPacket()
                if err != nil {
                    continue
                }
                networkLayer := packet.NetworkLayer()
                transportLayer := packet.TransportLayer()
                if networkLayer != nil && transportLayer != nil {
                    // 判断是否为TCP协议
                    if transportLayer.LayerType() == layers.LayerTypeTCP {
                        tcpLayer := transportLayer.(*layers.TCP)
                        // 判断是否为SYN-ACK响应
                        if tcpLayer.SYN && tcpLayer.ACK {
                            srcIP := networkLayer.NetworkFlow().Src().String()
                            dstIP := networkLayer.NetworkFlow().Dst().String()
                            srcPort := tcpLayer.SrcPort.String()
                            dstPort := tcpLayer.DstPort.String()
                            log.Logger.Debugf("SYN-ACK Response: %s:%s -> %s:%sn", srcIP, srcPort, dstIP, dstPort)

                            }
                            
                        }
                    }
                }
            }
        }

其他的如udp icmp都是大同小异的

继续学习

更多的事例 官方已经给了事例 地址在这里 https://github.com/google/gopacket/tree/master/examples

比如官方这里就给了synscan扫描

https://github.com/google/gopacket/tree/master/examples/synscan

学习gopacket的核心还是在于对网络协议的理解

原文始发于微信公众号(造点轮子):安全开发之 gopacket使用技巧

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月3日04:59:39
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   安全开发之 gopacket使用技巧https://cn-sec.com/archives/2095890.html

发表评论

匿名网友 填写信息