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)
}
}
}